diff --git a/.eslintignore b/.eslintignore index cd4efd8e..1f6dfa0a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,4 @@ *.d.ts +dist +node_modules +*.config.js diff --git a/.eslintrc.js b/.eslintrc.js index 02a609dc..f49834cc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,11 +1,80 @@ module.exports = { extends: ['expensify', 'prettier'], - rules: { - // Allow JSX to be written in any file ignoring the extension type - 'react/jsx-filename-extension': 'off' - }, - plugins: ['jest'], - env: { - "jest/globals": true - } + parser: '@typescript-eslint/parser', + overrides: [ + { + files: ['*.js', '*.jsx'], + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + }, + }, + rules: { + // Allow JSX to be written in any file ignoring the extension type + 'react/jsx-filename-extension': 'off', + 'rulesdir/no-api-in-views': 'off', + 'rulesdir/no-multiple-api-calls': 'off', + 'rulesdir/prefer-import-module-contents': 'off', + 'no-constructor-return': 'off', + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + }, + }, + { + files: ['*.ts', '*.tsx'], + extends: [ + 'expensify', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/stylistic', + 'plugin:import/typescript', + 'plugin:you-dont-need-lodash-underscore/all', + 'prettier', + 'plugin:prettier/recommended', + ], + plugins: ['react', 'import', '@typescript-eslint'], + parserOptions: { + project: './tsconfig.json', + }, + rules: { + 'prefer-regex-literals': 'off', + 'rulesdir/prefer-underscore-method': 'off', + 'react/jsx-props-no-spreading': 'off', + 'react/require-default-props': 'off', + 'valid-jsdoc': 'off', + 'es/no-optional-chaining': 'off', + 'es/no-nullish-coalescing-operators': 'off', + 'react/jsx-filename-extension': ['error', {extensions: ['.tsx', '.jsx']}], + 'import/no-unresolved': 'error', + 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], + 'no-use-before-define': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-unused-vars': ['error', {argsIgnorePattern: '^_'}], + '@typescript-eslint/consistent-type-imports': ['error', {prefer: 'type-imports'}], + '@typescript-eslint/consistent-type-exports': ['error', {fixMixedExportsWithInlineTypeSpecifier: false}], + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/array-type': ['error', {default: 'array-simple'}], + '@typescript-eslint/consistent-type-definitions': 'off', + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + }, + }, + ], }; diff --git a/.github/OSBotify-private-key.asc.gpg b/.github/OSBotify-private-key.asc.gpg new file mode 100644 index 00000000..c19d5c97 Binary files /dev/null and b/.github/OSBotify-private-key.asc.gpg differ diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 71bab56f..a24f8b13 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -27,3 +27,12 @@ jobs: - run: npm run lint env: CI: true + + - name: Verify there's no Prettier diff + run: | + npm run prettier -- --loglevel silent + if ! git diff --name-only --exit-code; then + # shellcheck disable=SC2016 + echo 'Error: Prettier diff detected! Please run `npm run prettier` and commit the changes.' + exit 1 + fi diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..baa8fded --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,77 @@ +name: Publish package to npmjs + +# This workflow runs when code is pushed to `main` (i.e: when a pull request is merged) +on: + push: + branches: [main] + +# Ensure that only once instance of this workflow executes at a time. +# If multiple PRs are merged in quick succession, there will only ever be one publish workflow running and one pending. +concurrency: ${{ github.workflow }} + +jobs: + version: + runs-on: ubuntu-latest + + # OSBotify will update the version on `main`, so this check is important to prevent an infinite loop + if: ${{ github.actor != 'OSBotify' }} + + steps: + - uses: actions/checkout@v4 + with: + ref: main + # The OS_BOTIFY_COMMIT_TOKEN is a personal access token tied to osbotify, which allows him to push to protected branches + token: ${{ secrets.OS_BOTIFY_COMMIT_TOKEN }} + + - name: Decrypt & Import OSBotify GPG key + run: | + cd .github + gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output OSBotify-private-key.asc OSBotify-private-key.asc.gpg + gpg --import OSBotify-private-key.asc + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + - name: Set up git for OSBotify + run: | + git config --global user.signingkey 367811D53E34168C + git config --global commit.gpgsign true + git config --global user.name OSBotify + git config --global user.email infra+osbotify@expensify.com + + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + registry-url: 'https://registry.npmjs.org' + + - name: Install npm packages + run: npm ci + + - name: Update npm version + run: npm version patch + + - name: Set new version in GitHub ENV + run: echo "NEW_VERSION=$(jq '.version' package.json)" >> $GITHUB_ENV + + - name: Push branch and publish tags + run: git push origin main && git push --tags + + - name: Build package + run: npm run build + + - name: Publish to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Get merged pull request + id: getMergedPullRequest + run: | + read -r number < <(gh pr list --search ${{ github.sha }} --state merged --json 'number' | jq -r '.[0] | [.number] | join(" ")') + echo "number=$number" >> "$GITHUB_OUTPUT" + env: + GITHUB_TOKEN: ${{ github.token }} + + - name: Comment on merged pull request + run: gh pr comment ${{ steps.getMergedPullRequest.outputs.number }} --body "🚀Published to npm in v${{ env.NEW_VERSION }}" + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 00000000..09d4a38f --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,24 @@ +name: TypeScript Checks + +on: + pull_request: + types: [opened, synchronize] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + cache: npm + cache-dependency-path: package-lock.json + + - run: npm ci + + - name: Type check with TypeScript + run: npm run typecheck + env: + CI: true diff --git a/.gitignore b/.gitignore index 608a5535..78c6fc53 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,10 @@ npm-debug.log package.json-e .DS_Store *.swp +dist + +# Decrypted private key we do not want to commit +.github/OSBotify-private-key.asc + +# Published package +*.tgz diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..62d44807 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20.13.0 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..d54e0bdf --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist +package.json +package-lock.json +*.html diff --git a/README.md b/README.md index 11baaf0b..55801113 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # `expensify-common` -This is a collection of JS libraries and components which are used across various Expensify projects. These libraries are provided as-is, and the repos which use them will need to do their own bundling, minifying, and uglifying. +This is a collection of JS/TS libraries and components which are used across various Expensify projects. These libraries are provided as-is, and the repos which use them will need to do their own bundling, minifying, and uglifying. # Installation -1. Clone this repo to a directory of your choosing -2. Run `npm install` to install all the dependencies +`expensify-common` is published to [`npm`](https://www.npmjs.com/package/expensify-common) + +```shell +npm install expensify-common +``` # Development * Write all code as ES6. -* Always lint your code with `npm run grunt watch` -* Make sure you're using http://editorconfig.org/ +* Always lint your code with `npm run lint` ## Testing your code while you are developing The best way to test your code while you are developing changes is via `npm link`. @@ -28,7 +30,7 @@ Alternatively, you can edit files directly in a project's `node_modules` then ap 1. They will review and accept your changes, merge them, then deploy a new version # Deploying a Change (Expensify Only) -Once the PR has been merged, update the `package.json` commit hash in any repos with a dependency on the code being changed in expensify-common, don't forget to run a `npm install` so `package-lock.json` is also updated. Be sure to check the repos below to confirm whether or not they are affected by your changes! +Once the PR has been merged, install the new version of the package with `npm install expensify-common@x.x.x` command. Be sure to check the repos below to confirm whether or not they are affected by your changes! - Expensify/Web-Expensify - Expensify/Web-Secure - Expensify/Mobile-Expensify diff --git a/__tests__/ExpensiMark-HTMLToText-test.js b/__tests__/ExpensiMark-HTMLToText-test.js index 39f5bebc..6757803b 100644 --- a/__tests__/ExpensiMark-HTMLToText-test.js +++ b/__tests__/ExpensiMark-HTMLToText-test.js @@ -151,7 +151,7 @@ test('Mention user html to text', () => { expect(parser.htmlToText(testString)).toBe('@Hidden'); const extras = { - accountIdToName: { + accountIDToName: { '1234': 'user@domain.com', }, }; @@ -180,7 +180,7 @@ test('Mention report html to text', () => { expect(parser.htmlToText(testString)).toBe('#Hidden'); const extras = { - reportIdToName: { + reportIDToName: { '1234': '#room-name', }, }; diff --git a/__tests__/ExpensiMark-Markdown-test.js b/__tests__/ExpensiMark-Markdown-test.js index 0f8e8831..093f5ca5 100644 --- a/__tests__/ExpensiMark-Markdown-test.js +++ b/__tests__/ExpensiMark-Markdown-test.js @@ -497,6 +497,18 @@ test('map div with quotes', () => { expect(parser.htmlToMarkdown(testString)).toBe(resultString); }); +test('double quotes in same line', () => { + const testString = '
line 1
'; + const resultString = '>> line 1'; + expect(parser.htmlToMarkdown(testString)).toBe(resultString); +}); + +test('triple quotes in same line', () => { + const testString = '
line 1
'; + const resultString = '>>> line 1'; + expect(parser.htmlToMarkdown(testString)).toBe(resultString); +}); + test('map table to newline', () => { const testString = 'line 1line 2'; const resultString = 'line 1\nline 2'; @@ -756,12 +768,16 @@ test('Mention user html to markdown', () => { testString = '@user@DOMAIN.com'; expect(parser.htmlToMarkdown(testString)).toBe('@user@DOMAIN.com'); + // When there is a phone number mention the sms domain `@expensify.sms`should be removed from returned string + testString = '@+311231231@expensify.sms'; + expect(parser.htmlToMarkdown(testString)).toBe('@+311231231'); + // When there is `accountID` and no `extras`, `@Hidden` should be returned testString = ''; expect(parser.htmlToMarkdown(testString)).toBe('@Hidden'); const extras = { - accountIdToName: { + accountIDToName: { '1234': 'user@domain.com', }, }; @@ -790,7 +806,7 @@ test('Mention report html to markdown', () => { expect(parser.htmlToText(testString)).toBe('#Hidden'); const extras = { - reportIdToName: { + reportIDToName: { '1234': '#room-name', }, }; diff --git a/babel.config.js b/babel.config.js index d7c97f55..f792e8e1 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,12 +1,3 @@ module.exports = { - presets: [ - [ - '@babel/preset-env', - { - targets: { - node: 'current', - }, - }, - ], - ], + presets: [['@babel/preset-env', {targets: {node: 'current'}}], '@babel/preset-typescript'], }; diff --git a/lib/API.jsx b/lib/API.jsx index 6a6d0482..5a98186f 100644 --- a/lib/API.jsx +++ b/lib/API.jsx @@ -8,6 +8,7 @@ import _ from 'underscore'; // Use this deferred lib so we don't have a dependency on jQuery (so we can use this module in mobile) import {Deferred} from 'simply-deferred'; import ExpensifyAPIDeferred from './APIDeferred'; +import {isWindowAvailable} from './utils'; /** * @param {Network} network @@ -47,7 +48,7 @@ export default function API(network, args) { network .get('/revision.txt') .done((codeRevision) => { - if (codeRevision.trim() === window.CODE_REVISION) { + if (isWindowAvailable() && codeRevision.trim() === window.CODE_REVISION) { console.debug('Code revision is up to date'); promise.resolve(); } else { @@ -150,6 +151,7 @@ export default function API(network, args) { * @param {String} commandName The name of the API command */ function requireParameters(parameterNames, parameters, commandName) { + // eslint-disable-next-line rulesdir/prefer-early-return parameterNames.forEach((parameterName) => { if (!_(parameters).has(parameterName) || parameters[parameterName] === null || parameters[parameterName] === undefined) { const parametersCopy = _.clone(parameters); @@ -823,7 +825,7 @@ export default function API(network, args) { * * @returns {APIDeferred} */ - createAdminIssuedVirtualCard: function (parameters) { + createAdminIssuedVirtualCard(parameters) { const commandName = 'Card_CreateAdminIssuedVirtualCard'; requireParameters(['cardTitle', 'assigneeEmail', 'cardLimit', 'cardLimitType', 'domainName'], parameters, commandName); return performPOSTRequest(commandName, parameters); @@ -842,7 +844,7 @@ export default function API(network, args) { * * @returns {APIDeferred} */ - editAdminIssuedVirtualCard: function (parameters) { + editAdminIssuedVirtualCard(parameters) { const commandName = 'Card_EditAdminIssuedVirtualCard'; requireParameters(['domainName', 'cardID', 'cardTitle', 'assigneeEmail', 'cardLimit', 'cardLimitType'], parameters, commandName); return performPOSTRequest(commandName, parameters); diff --git a/lib/BrowserDetect.jsx b/lib/BrowserDetect.jsx index 2f5bfb60..63894f96 100644 --- a/lib/BrowserDetect.jsx +++ b/lib/BrowserDetect.jsx @@ -1,3 +1,5 @@ +import {isNavigatorAvailable, isWindowAvailable} from './utils'; + const BROWSERS = { EDGE: 'Edge', CHROME: 'Chrome', @@ -13,6 +15,10 @@ const MOBILE_PLATFORMS = { }; function searchString() { + if (!isWindowAvailable() || !isNavigatorAvailable()) { + return ''; + } + const data = [ { string: navigator.userAgent, @@ -72,6 +78,10 @@ function searchString() { } function getMobileDevice() { + if (!isNavigatorAvailable() || !navigator.userAgent) { + return ''; + } + const data = [ { devices: ['iPhone', 'iPad', 'iPod'], diff --git a/lib/CONST.d.ts b/lib/CONST.d.ts deleted file mode 100644 index 77974b46..00000000 --- a/lib/CONST.d.ts +++ /dev/null @@ -1,852 +0,0 @@ -/** - * URL of our CloudFront Instance - */ -export declare const g_cloudFront: 'https://d2k5nsl2zxldvw.cloudfront.net'; -/** - * URL of our image CDN - */ -export declare const g_cloudFrontImg: 'https://d2k5nsl2zxldvw.cloudfront.net/images/'; -export declare const CONST: { - readonly CORPAY_DIRECT_REIMBURSEMENT_CURRENCIES: readonly ['USD', 'GBP', 'EUR', 'AUD', 'CAD']; - /** - * Default max ACH limit. It can be overwritten by a private NVP - */ - readonly ACH_DEFAULT_MAX_AMOUNT_LIMIT: 2000000; - /** - * IRS remimbursement rate for mileage - * WARNING ! UPDATE THE PHP CONSTANT VERSION WHEN UPDATING THIS ONE - */ - readonly MILEAGE_IRS_RATE: 0.545 | 0.58; - readonly COUNTRY: { - readonly US: 'US'; - readonly AU: 'AU'; - readonly UK: 'UK'; - readonly NZ: 'NZ'; - }; - readonly CURRENCIES: { - readonly US: 'USD'; - readonly AU: 'AUD'; - readonly UK: 'GBP'; - readonly NZ: 'NZD'; - }; - readonly STATES: { - readonly AK: { - readonly stateISO: 'AK'; - readonly stateName: 'Alaska'; - }; - readonly AL: { - readonly stateISO: 'AL'; - readonly stateName: 'Alabama'; - }; - readonly AR: { - readonly stateISO: 'AR'; - readonly stateName: 'Arkansas'; - }; - readonly AZ: { - readonly stateISO: 'AZ'; - readonly stateName: 'Arizona'; - }; - readonly CA: { - readonly stateISO: 'CA'; - readonly stateName: 'California'; - }; - readonly CO: { - readonly stateISO: 'CO'; - readonly stateName: 'Colorado'; - }; - readonly CT: { - readonly stateISO: 'CT'; - readonly stateName: 'Connecticut'; - }; - readonly DE: { - readonly stateISO: 'DE'; - readonly stateName: 'Delaware'; - }; - readonly FL: { - readonly stateISO: 'FL'; - readonly stateName: 'Florida'; - }; - readonly GA: { - readonly stateISO: 'GA'; - readonly stateName: 'Georgia'; - }; - readonly HI: { - readonly stateISO: 'HI'; - readonly stateName: 'Hawaii'; - }; - readonly IA: { - readonly stateISO: 'IA'; - readonly stateName: 'Iowa'; - }; - readonly ID: { - readonly stateISO: 'ID'; - readonly stateName: 'Idaho'; - }; - readonly IL: { - readonly stateISO: 'IL'; - readonly stateName: 'Illinois'; - }; - readonly IN: { - readonly stateISO: 'IN'; - readonly stateName: 'Indiana'; - }; - readonly KS: { - readonly stateISO: 'KS'; - readonly stateName: 'Kansas'; - }; - readonly KY: { - readonly stateISO: 'KY'; - readonly stateName: 'Kentucky'; - }; - readonly LA: { - readonly stateISO: 'LA'; - readonly stateName: 'Louisiana'; - }; - readonly MA: { - readonly stateISO: 'MA'; - readonly stateName: 'Massachusetts'; - }; - readonly MD: { - readonly stateISO: 'MD'; - readonly stateName: 'Maryland'; - }; - readonly ME: { - readonly stateISO: 'ME'; - readonly stateName: 'Maine'; - }; - readonly MI: { - readonly stateISO: 'MI'; - readonly stateName: 'Michigan'; - }; - readonly MN: { - readonly stateISO: 'MN'; - readonly stateName: 'Minnesota'; - }; - readonly MO: { - readonly stateISO: 'MO'; - readonly stateName: 'Missouri'; - }; - readonly MS: { - readonly stateISO: 'MS'; - readonly stateName: 'Mississippi'; - }; - readonly MT: { - readonly stateISO: 'MT'; - readonly stateName: 'Montana'; - }; - readonly NC: { - readonly stateISO: 'NC'; - readonly stateName: 'North Carolina'; - }; - readonly ND: { - readonly stateISO: 'ND'; - readonly stateName: 'North Dakota'; - }; - readonly NE: { - readonly stateISO: 'NE'; - readonly stateName: 'Nebraska'; - }; - readonly NH: { - readonly stateISO: 'NH'; - readonly stateName: 'New Hampshire'; - }; - readonly NJ: { - readonly stateISO: 'NJ'; - readonly stateName: 'New Jersey'; - }; - readonly NM: { - readonly stateISO: 'NM'; - readonly stateName: 'New Mexico'; - }; - readonly NV: { - readonly stateISO: 'NV'; - readonly stateName: 'Nevada'; - }; - readonly NY: { - readonly stateISO: 'NY'; - readonly stateName: 'New York'; - }; - readonly OH: { - readonly stateISO: 'OH'; - readonly stateName: 'Ohio'; - }; - readonly OK: { - readonly stateISO: 'OK'; - readonly stateName: 'Oklahoma'; - }; - readonly OR: { - readonly stateISO: 'OR'; - readonly stateName: 'Oregon'; - }; - readonly PA: { - readonly stateISO: 'PA'; - readonly stateName: 'Pennsylvania'; - }; - readonly PR: { - readonly stateISO: 'PR'; - readonly stateName: 'Puerto Rico'; - }; - readonly RI: { - readonly stateISO: 'RI'; - readonly stateName: 'Rhode Island'; - }; - readonly SC: { - readonly stateISO: 'SC'; - readonly stateName: 'South Carolina'; - }; - readonly SD: { - readonly stateISO: 'SD'; - readonly stateName: 'South Dakota'; - }; - readonly TN: { - readonly stateISO: 'TN'; - readonly stateName: 'Tennessee'; - }; - readonly TX: { - readonly stateISO: 'TX'; - readonly stateName: 'Texas'; - }; - readonly UT: { - readonly stateISO: 'UT'; - readonly stateName: 'Utah'; - }; - readonly VA: { - readonly stateISO: 'VA'; - readonly stateName: 'Virginia'; - }; - readonly VT: { - readonly stateISO: 'VT'; - readonly stateName: 'Vermont'; - }; - readonly WA: { - readonly stateISO: 'WA'; - readonly stateName: 'Washington'; - }; - readonly WI: { - readonly stateISO: 'WI'; - readonly stateName: 'Wisconsin'; - }; - readonly WV: { - readonly stateISO: 'WV'; - readonly stateName: 'West Virginia'; - }; - readonly WY: { - readonly stateISO: 'WY'; - readonly stateName: 'Wyoming'; - }; - readonly DC: { - readonly stateISO: 'DC'; - readonly stateName: 'District Of Columbia'; - }; - }; - /** - * Store all the regular expression we are using for matching stuff - */ - readonly REG_EXP: { - /** - * Regular expression to check that a domain is valid - */ - readonly DOMAIN: RegExp; - /** - * Regex matching an text containing an email - */ - readonly EMAIL_PART: "([\\w\\-\\+\\'#]+(?:\\.[\\w\\-\\'\\+]+)*@(?:[\\w\\-]+\\.)+[a-z]{2,})"; - /** - * Regex matching a text containing general phone number - */ - readonly GENERAL_PHONE_PART: RegExp; - /** - * Regex matching a text containing an E.164 format phone number - */ - readonly PHONE_PART: '\\+[1-9]\\d{1,14}'; - /** - * Regular expression to check that a basic name is valid - */ - readonly FREE_NAME: RegExp; - /** - * Regular expression to check that a card is masked - */ - readonly MASKED_CARD: RegExp; - /** - * Regular expression to check that an email is valid - */ - readonly EMAIL: RegExp; - /** - * Regular expression to extract an email from a text - */ - readonly EXTRACT_EMAIL: RegExp; - /** - * Regular expression to search for valid email addresses in a string - */ - readonly EMAIL_SEARCH: RegExp; - /** - * Regular expression to detect if something is a hyperlink - * - * Adapted from: https://gist.github.com/dperini/729294 - */ - readonly HYPERLINK: RegExp; - /** - * Regex to match valid emails during markdown transformations - */ - readonly MARKDOWN_EMAIL: "([a-zA-Z0-9.!#$%&'+/=?^`{|}-][a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]*@[a-zA-Z0-9-]+?(\\.[a-zA-Z]+)+)"; - /** - * Regex matching an text containing an Emoji - */ - readonly EMOJIS: RegExp; - /** - * Regex matching an text containing an Emoji that can be a single emoji or made up by some different emojis - * - * @type RegExp - */ - readonly EMOJI_RULE: RegExp; - }; - readonly REPORT: { - /** - * Limit when we decided to turn off print to pdf and use only the native feature - */ - readonly LIMIT_PRINT_PDF: 250; - readonly ACH_LIMIT: 2000000; - readonly ACH_DEFAULT_DAYS: 4; - /** - * This is the string that a user can enter in a formula to refer to the report title field - */ - readonly TITLE_FORMULA: '{report:title}'; - /** - * The max time a comment can be made after another to be considered the same comment, in seconds - */ - readonly MAX_AGE_SAME_COMMENT: 300; - readonly SMARTREPORT_AGENT_EMAIL: 'smartreports@expensify.com'; - }; - /** - * Root URLs - */ - readonly URL: { - readonly FORUM_ROOT: 'https://community.expensify.com/'; - readonly RECEIPTS: { - readonly DEVELOPMENT: 'https://www.expensify.com.dev/receipts/'; - readonly STAGING: 'https://staging.expensify.com/receipts/'; - readonly PRODUCTION: 'https://www.expensify.com/receipts/'; - }; - readonly CLOUDFRONT: 'https://d2k5nsl2zxldvw.cloudfront.net'; - readonly CLOUDFRONT_IMG: 'https://d2k5nsl2zxldvw.cloudfront.net/images/'; - readonly CLOUDFRONT_FILES: 'https://d2k5nsl2zxldvw.cloudfront.net/files/'; - readonly EXPENSIFY_SYNC_MANAGER: 'quickbooksdesktop/Expensify_QuickBooksDesktop_Setup_2300802.exe'; - readonly USEDOT_ROOT: 'https://use.expensify.com/'; - readonly ITUNES_SUBSCRIPTION: 'https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/manageSubscriptions'; - }; - readonly DATE: { - readonly FORMAT_STRING: 'yyyy-MM-dd'; - readonly FORMAT_STRING_PRETTY: 'MMM d, yyyy'; - /** - * Expensify date format string for moment js - * usage: moment().format( CONST.DATE.MOMENT_FORMAT_STRING ) - */ - readonly MOMENT_FORMAT_STRING: 'YYYY-MM-DD'; - /** - * This is a typical format of the date plus the time - */ - readonly MOMENT_DATE_TIME: 'YYYY-MM-DD HH:mm'; - /** - * Pretty format used for report history items - * - * @example Jun 19, 2019 12:38 PM - */ - readonly MOMENT_DATE_TIME_PRETTY: 'MMM DD YYYY h:mma'; - /** - * Date-time format, including timezone information, eg "2015-10-14T19:44:35+07:00" - */ - readonly MOMENT_DATE_TIME_TIMEZONE: 'YYYY-MM-DDTHH:mm:ssZ'; - /** - * Moment formatting option for a date of this format "Jul 2, 2014" - */ - readonly MOMENT_US_DATE: 'MMM D, YYYY'; - /** - * Moment formatting option for a date of this format "July 2, 2014" - * ie, full month name - */ - readonly MOMENT_US_DATE_LONG: 'MMMM D, YYYY'; - /** - * Moment formatting option for full month name and year as in "July 2015" - */ - readonly MOMENT_US_MONTH_YEAR_LONG: 'MMMM YYYY'; - /** - * Difference between the local time and UTC time in ms - */ - readonly TIMEZONE_OFFSET_MS: number; - readonly SHORT_MONTH_SHORT_DAY: 'MMM d'; - readonly LONG_YEAR_MONTH_DAY_24_TIME: 'yyyy-MM-dd HH:mm:ss'; - readonly SHORT_MONTH_DAY_LOCAL_TIME: 'MMM D [at] LT'; - readonly SHORT_MONTH_DAY_YEAR_LOCAL_TIME: 'MMM D, YYYY [at] LT'; - }; - /** - * Message used by the Func.die() exception - */ - readonly FUNC_DIE_MESSAGE: 'Aborting JavaScript execution'; - /** - * Default for how long the email delivery failure NVP should be valid (in seconds) - * Currently 14 days (14 * 24 * 60 * 60) - * - * WARNING ! UPDATE THE PHP CONSTANT VERSION WHEN UPDATING THIS ONE - */ - readonly EMAIL_DELIVERY_FAILURE_VALIDITY: 1209600; - /** - * Bill Processing-related constants - */ - readonly BILL_PROCESSING_PARTNER_NAME: 'expensify.cash'; - readonly BILL_PROCESSING_EMAIL_DOMAIN: 'expensify.cash'; - /** - * Bank Import Logic Constants - */ - readonly BANK_IMPORT: { - readonly BANK_STATUS_BROKEN: 2; - }; - /** - * Bank Account Logic Constants - */ - readonly BANK_ACCOUNT: { - readonly VERIFICATION_MAX_ATTEMPTS: 7; - }; - /** - * Emails that the user shouldn't be interacting with from the front-end interface - * Trying to add these emails as a delegate, onto a policy, or as an approver is considered invalid - * Any changes here should be reflected in the PHP constant in web-expensify, - * which is located in _constant.php and also named EXPENSIFY_EMAILS. - * And should also be reflected in the constant in expensify/app, - * which is located in src/CONST.js and also named EXPENSIFY_EMAILS. - */ - readonly EXPENSIFY_EMAILS: readonly [ - 'concierge@expensify.com', - 'help@expensify.com', - 'receipts@expensify.com', - 'chronos@expensify.com', - 'qa@expensify.com', - 'contributors@expensify.com', - 'firstresponders@expensify.com', - 'qa+travisreceipts@expensify.com', - 'bills@expensify.com', - 'studentambassadors@expensify.com', - 'accounting@expensify.com', - 'payroll@expensify.com', - 'svfg@expensify.com', - 'integrationtestingcreds@expensify.com', - 'admin@expensify.com', - 'notifications@expensify.com', - ]; - /** - * Emails that the user shouldn't submit reports to nor share reports with - * Any changes here should be reflected in the PHP constant, - * which is located in _constant.php and also named INVALID_APPROVER_AND_SHAREE_EMAILS - */ - readonly INVALID_APPROVER_AND_SHAREE_EMAILS: readonly [ - 'concierge@expensify.com', - 'help@expensify.com', - 'receipts@expensify.com', - 'chronos@expensify.com', - 'qa@expensify.com', - 'contributors@expensify.com', - 'firstresponders@expensify.com', - 'qa+travisreceipts@expensify.com', - 'bills@expensify.com', - 'admin@expensify.com', - 'notifications@expensify.com', - ]; - /** - * Smart scan-related constants - */ - readonly SMART_SCAN: { - readonly COST: 20; - readonly FREE_NUMBER: 25; - }; - readonly SMS: { - readonly DOMAIN: 'expensify.sms'; - readonly E164_REGEX: RegExp; - }; - readonly PASSWORD_COMPLEXITY_REGEX_STRING: '^(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z]).{8,}$'; - readonly INTEGRATIONS: { - /** - * Constants that specify how to map (import) Integrations data to Expensify - * Parallel to IntegrationEntityMappingTypeEnum in the IS - */ - readonly DATA_MAPPING: { - readonly NONE: 'NONE'; - readonly TAG: 'TAG'; - readonly REPORT_FIELD: 'REPORT_FIELD'; - readonly DEFAULT: 'DEFAULT'; - }; - readonly EXPORT_DATE: { - readonly LAST_EXPENSE: 'LAST_EXPENSE'; - readonly REPORT_EXPORTED: 'REPORT_EXPORTED'; - readonly REPORT_SUBMITTED: 'REPORT_SUBMITTED'; - }; - readonly XERO_HQ_CONNECTION_NAME: 'xerohq'; - readonly EXPENSIFY_SYNC_MANAGER_VERSION: '23.0.802.0'; - }; - readonly INTEGRATION_TYPES: { - readonly ACCOUNTING: 'accounting'; - readonly HR: 'hr'; - }; - readonly DIRECT_INTEGRATIONS: { - readonly zenefits: { - readonly value: 'zenefits'; - readonly text: 'Zenefits'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/zenefit.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/zenefit_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/zenefit_alert.svg'; - readonly types: readonly ['hr']; - readonly isCorporateOnly: false; - }; - readonly gusto: { - readonly value: 'gusto'; - readonly text: 'Gusto'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/gusto.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/gusto_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/gusto_alert.svg'; - readonly types: readonly ['hr']; - readonly isCorporateOnly: false; - }; - readonly quickbooksOnline: { - readonly value: 'quickbooksOnline'; - readonly text: 'QuickBooks Online'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/quickbooks.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/quickbooks_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/quickbooks_alert.svg'; - readonly types: readonly ['hr', 'accounting']; - readonly isCorporateOnly: false; - }; - readonly xero: { - readonly value: 'xero'; - readonly text: 'Xero'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/xero.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/xero_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/xero_alert.svg'; - readonly types: readonly ['accounting']; - readonly isCorporateOnly: false; - }; - readonly netsuite: { - readonly value: 'netsuite'; - readonly text: 'NetSuite'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/netsuite.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/netsuite_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/netsuite_alert.svg'; - readonly types: readonly ['hr', 'accounting']; - readonly isCorporateOnly: true; - }; - readonly quickbooksDesktop: { - readonly value: 'qbd'; - readonly text: 'QuickBooks Desktop'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/quickbooks.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/quickbooks_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/quickbooks_alert.svg'; - readonly types: readonly ['accounting']; - readonly isCorporateOnly: false; - }; - readonly intacct: { - readonly value: 'intacct'; - readonly text: 'Sage Intacct'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/sage.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/sage_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/sage_alert.svg'; - readonly types: readonly ['hr', 'accounting']; - readonly isCorporateOnly: true; - }; - readonly financialforce: { - readonly value: 'financialforce'; - readonly text: 'Certinia'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/certinia.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/certinia_gray.svg'; - readonly alert_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/certinia_alert.svg'; - readonly types: readonly ['accounting']; - readonly isCorporateOnly: true; - }; - }; - readonly INDIRECT_INTEGRATIONS: { - readonly microsoft_dynamics: { - readonly value: 'microsoft_dynamics'; - readonly text: 'Microsoft Dynamics'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/microsoft_dynamics.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/microsoft_dynamics_gray.svg'; - readonly types: readonly ['accounting']; - readonly isCorporateOnly: true; - }; - readonly oracle: { - readonly value: 'oracle'; - readonly text: 'Oracle'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/oracle.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/oracle_gray.svg'; - readonly types: readonly ['hr', 'accounting']; - readonly isCorporateOnly: true; - }; - readonly sage: { - readonly value: 'sage'; - readonly text: 'Sage'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/sage.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/sage_gray.svg'; - readonly types: readonly ['accounting']; - readonly isCorporateOnly: true; - }; - readonly sap: { - readonly value: 'sap'; - readonly text: 'SAP'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/sap.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/sap_gray.svg'; - readonly types: readonly ['accounting']; - readonly isCorporateOnly: true; - }; - readonly myob: { - readonly value: 'myob'; - readonly text: 'MYOB'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/myob.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/myob_gray.svg'; - readonly types: readonly ['accounting']; - readonly isCorporateOnly: true; - }; - readonly workday: { - readonly value: 'workday'; - readonly text: 'Workday'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/workday.svg'; - readonly gray_image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/workday_gray.svg'; - readonly types: readonly ['hr']; - readonly isCorporateOnly: true; - }; - readonly adp: { - readonly value: 'adp'; - readonly text: 'ADP'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/export-icons/adp.svg'; - readonly types: readonly ['hr']; - readonly isCorporateOnly: true; - }; - readonly generic_indirect_connection: { - readonly value: 'generic_indirect_connection'; - readonly text: 'Other'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - readonly types: readonly ['hr', 'accounting']; - }; - }; - readonly DEFAULT_IS_TEMPLATES: { - readonly default: { - readonly value: 'default_template'; - readonly text: 'Basic Export'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - }; - readonly tag: { - readonly value: 'tag_template'; - readonly text: 'Tag Export'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - }; - readonly category: { - readonly value: 'category_template'; - readonly text: 'Category Export'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - }; - readonly detailed: { - readonly value: 'detailed_export'; - readonly text: 'All Data - Expense Level Export'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - }; - readonly report: { - readonly value: 'report_level_export'; - readonly text: 'All Data - Report Level Export'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - }; - readonly tax: { - readonly value: 'multiple_tax_export'; - readonly text: 'Canadian Multiple Tax Export'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - }; - readonly perdiem: { - readonly value: 'per_diem_export'; - readonly text: 'Per Diem Export'; - readonly image: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/accounting-other--blue.svg'; - }; - }; - readonly NVP: { - readonly DISMISSED_VIOLATIONS: 'dismissedViolations'; - }; - readonly FILESIZE: { - readonly BYTES_IN_MEGABYTE: 1000000; - readonly MAX: 10000000; - }; - readonly PARTNER_NAMES: { - readonly IPHONE: 'iphone'; - readonly ANDROID: 'android'; - readonly CHAT: 'chat-expensify-com'; - }; - readonly LOGIN_TYPES: { - readonly WEB: 'login'; - readonly MOBILE: 'device'; - }; - readonly EXPENSIFY_CARD: { - readonly FEED_NAME: 'Expensify Card'; - readonly FRAUD_STATES: { - readonly NONE: 0; - readonly DOMAIN_CARDS_REIMBURSEMENTS_INVESTIGATION: 1; - readonly DOMAIN_CARDS_RAPID_INCREASE_INVESTIGATION: 2; - readonly DOMAIN_CARDS_RAPID_INCREASE_CLEARED: 3; - readonly DOMAIN_CARDS_RAPID_INCREASE_CONFIRMED: 4; - readonly INDIVIDUAL_CARD_RAPID_INCREASE_INVESTIGATION: 5; - readonly INDIVIDUAL_CARD_RAPID_INCREASE_CLEARED: 6; - readonly INDIVIDUAL_CARD_RAPID_INCREASE_CONFIRMED: 7; - readonly SUSPICIOUS_PAN_ENTRY: 8; - readonly SUSPICIOUS_PAN_ENTRY_CLEARED: 9; - readonly SUSPICIOUS_PAN_ENTRY_CONFIRMED: 10; - }; - }; - readonly TRAVEL_BOOKING: { - readonly OPTIONS: { - readonly shortFlightFare: { - readonly economy: 'Economy'; - readonly premiumEconomy: 'Premium Economy'; - readonly business: 'Business'; - readonly first: 'First'; - }; - readonly longFlightFare: { - readonly economy: 'Economy'; - readonly premiumEconomy: 'Premium Economy'; - readonly business: 'Business'; - readonly first: 'First'; - }; - readonly hotelStar: { - readonly oneStar: '1'; - readonly twoStars: '2'; - readonly threeStars: '3'; - readonly fourStars: '4'; - readonly fiveStars: '5'; - }; - }; - readonly DEFAULT_OPTIONS: { - readonly shortFlightFare: 'economy'; - readonly longFlightFare: 'economy'; - readonly hotelStar: 'fourStars'; - }; - }; - readonly EXPENSIFY_DOMAINS: readonly ['expensify.com', 'expensifail.com', 'expensicorp.com']; - readonly SUBSCRIPTION_CHANGE_REASONS: { - readonly TOO_LIMITED: { - readonly id: 'tooLimited'; - readonly label: 'Functionality needs improvement'; - readonly prompt: 'What software are you migrating to and what led to this decision?'; - }; - readonly TOO_EXPENSIVE: { - readonly id: 'tooExpensive'; - readonly label: 'Too expensive'; - readonly prompt: 'What software are you migrating to and what led to this decision?'; - }; - readonly INADEQUATE_SUPPORT: { - readonly id: 'inadequateSupport'; - readonly label: 'Inadequate customer support'; - readonly prompt: 'What software are you migrating to and what led to this decision?'; - }; - readonly BUSINESS_CLOSING: { - readonly id: 'businessClosing'; - readonly label: 'Company closing, downsizing, or acquired'; - readonly prompt: 'What software are you migrating to and what led to this decision?'; - }; - }; -}; -/** - * UI Constants - */ -export declare const UI: { - readonly ICON: { - readonly DELETE: 'trashcan'; - readonly CAR: 'car'; - readonly CASH: 'cash'; - readonly MANAGED_CARD: 'corporate-card'; - readonly CARD: 'credit-card'; - readonly CLOCK: 'time'; - readonly PER_DIEM: 'per-diem'; - readonly PENDING_CARD: 'card-transaction-pending'; - readonly CSV_UPLOAD: 'csv-upload'; - readonly PENDING_CREDIT_CARD: 'credit-card-pending'; - }; - readonly spinnerDIV: '
'; - readonly spinnerSmallDIV: '
'; - readonly spinnerLargeDIV: '
'; - readonly spinnerClass: 'view_spinner'; - readonly SPINNER: 'spinner'; - readonly imageURLPrefix: 'https://d2k5nsl2zxldvw.cloudfront.net/images/'; - readonly ACTIVE: 'active'; - readonly ERROR: 'error'; - readonly HIDDEN: 'hidden'; - readonly INVISIBLE: 'invisible'; - readonly DEPRECIATED: 'depreciated'; - readonly DISABLED: 'disabled'; - readonly REQUIRED: 'required'; - readonly SELECT_DEFAULT: '###'; - readonly SELECTED: 'selected'; - readonly QR_CODE: 'js_qrCode'; - readonly DIALOG_Z_INDEX: 4000; -}; -export declare const PUBLIC_DOMAINS: readonly [ - 'accountant.com', - 'afis.ch', - 'aol.com', - 'artlover.com', - 'asia.com', - 'att.net', - 'bellsouth.net', - 'bills.expensify.com', - 'btinternet.com', - 'cheerful.com', - 'chromeexpensify.com', - 'comcast.net', - 'consultant.com', - 'contractor.com', - 'cox.net', - 'cpa.com', - 'cryptohistoryprice.com', - 'dr.com', - 'email.com', - 'engineer.com', - 'europe.com', - 'evernote.user', - 'execs.com', - 'expensify.cash', - 'expensify.sms', - 'gmail.com', - 'gmail.con', - 'googlemail.com', - 'hey.com', - 'hotmail.co.uk', - 'hotmail.com', - 'hotmail.fr', - 'hotmail.it', - 'icloud.com', - 'iname.com', - 'jeeviess.com', - 'live.com', - 'mac.com', - 'mail.com', - 'mail.ru', - 'mailfence.com', - 'me.com', - 'msn.com', - 'musician.org', - 'myself.com', - 'outlook.com', - 'pm.me', - 'post.com', - 'privaterelay.appleid.com', - 'proton.me', - 'protonmail.ch', - 'protonmail.com', - 'qq.com', - 'rigl.ch', - 'sasktel.net', - 'sbcglobal.net', - 'spacehotline.com', - 'tafmail.com', - 'techie.com', - 'usa.com', - 'verizon.net', - 'vomoto.com', - 'wolfandcranebar.tech', - 'workmail.com', - 'writeme.com', - 'yahoo.ca', - 'yahoo.co.in', - 'yahoo.co.uk', - 'yahoo.com', - 'yahoo.com.br', - 'ymail.com', -]; diff --git a/lib/CONST.jsx b/lib/CONST.ts similarity index 98% rename from lib/CONST.jsx rename to lib/CONST.ts index 7991d1e2..7e112e4a 100644 --- a/lib/CONST.jsx +++ b/lib/CONST.ts @@ -8,14 +8,14 @@ const MOMENT_FORMAT_STRING = 'YYYY-MM-DD'; /** * URL of our CloudFront Instance */ -export const g_cloudFront = 'https://d2k5nsl2zxldvw.cloudfront.net'; +const g_cloudFront = 'https://d2k5nsl2zxldvw.cloudfront.net'; /** * URL of our image CDN */ -export const g_cloudFrontImg = `${g_cloudFront}/images/`; +const g_cloudFrontImg = `${g_cloudFront}/images/`; -export const CONST = { +const CONST = { CORPAY_DIRECT_REIMBURSEMENT_CURRENCIES: ['USD', 'GBP', 'EUR', 'AUD', 'CAD'], /** @@ -361,6 +361,7 @@ export const CONST = { * * @type RegExp */ + // eslint-disable-next-line no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, /** @@ -884,12 +885,12 @@ export const CONST = { prompt: 'What software are you migrating to and what led to this decision?', }, }, -}; +} as const; /** * UI Constants */ -export const UI = { +const UI = { ICON: { DELETE: 'trashcan', CAR: 'car', @@ -923,10 +924,10 @@ export const UI = { // Base z-index for dialogs $zindex-dialog in _vars.scss should take it's value from here! DIALOG_Z_INDEX: 4000, -}; +} as const; // List of most frequently used public domains -export const PUBLIC_DOMAINS = [ +const PUBLIC_DOMAINS = [ 'accountant.com', 'afis.ch', 'aol.com', @@ -998,4 +999,6 @@ export const PUBLIC_DOMAINS = [ 'yahoo.com', 'yahoo.com.br', 'ymail.com', -]; +] as const; + +export {g_cloudFront, g_cloudFrontImg, CONST, UI, PUBLIC_DOMAINS}; diff --git a/lib/Cookie.jsx b/lib/Cookie.jsx index 8dad1345..d2ae6f3c 100644 --- a/lib/Cookie.jsx +++ b/lib/Cookie.jsx @@ -58,7 +58,7 @@ function set(name, value, expiredays) { // Get expiry date, set const exdate = new Date(); exdate.setDate(exdate.getDate() + expiredays); - document.cookie = `${name}=${encodeURIComponent(value)}` + `${expiredays === null ? '' : `;expires=${exdate.toUTCString()}`}`; + document.cookie = `${name}=${encodeURIComponent(value)}${expiredays === null ? '' : `;expires=${exdate.toUTCString()}`}`; } /** diff --git a/lib/CredentialsWrapper.jsx b/lib/CredentialsWrapper.jsx index b6283c07..90fe7759 100644 --- a/lib/CredentialsWrapper.jsx +++ b/lib/CredentialsWrapper.jsx @@ -1,6 +1,6 @@ import localForage from 'localforage'; -export const LOGIN_PARTNER_DETAILS = { +const LOGIN_PARTNER_DETAILS = { CREDENTIALS_KEY: 'DEVICE_SESSION_CREDENTIALS', EXPENSIFY_PARTNER_PREFIX: 'expensify.', PARTNER_NAME: 'chat-expensify-com', @@ -33,6 +33,7 @@ const CredentialWrapper = { */ setCredentials(credentials) { if (!credentials.partnerUserID || !credentials.partnerUserSecret) { + // eslint-disable-next-line prefer-promise-reject-errors return Promise.reject('Invalid credential pair'); } @@ -48,3 +49,4 @@ const CredentialWrapper = { }; export default CredentialWrapper; +export {LOGIN_PARTNER_DETAILS}; diff --git a/lib/Device.d.ts b/lib/Device.d.ts deleted file mode 100644 index 6cb8abd3..00000000 --- a/lib/Device.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare function getOSAndName(): { - os: string | undefined; - osVersion: string | undefined; - deviceName: string | undefined; - deviceVersion: string | undefined; -}; -export {getOSAndName}; diff --git a/lib/Device.jsx b/lib/Device.ts similarity index 65% rename from lib/Device.jsx rename to lib/Device.ts index 43419dba..1100cb45 100644 --- a/lib/Device.jsx +++ b/lib/Device.ts @@ -1,6 +1,13 @@ import {UAParser} from 'ua-parser-js'; -function getOSAndName() { +type DeviceInfo = { + os: string | undefined; + osVersion: string | undefined; + deviceName: string | undefined; + deviceVersion: string | undefined; +}; + +function getOSAndName(): DeviceInfo { const parser = new UAParser(); const result = parser.getResult(); return { diff --git a/lib/ExpenseRule.jsx b/lib/ExpenseRule.jsx index 5d614932..03c66be3 100644 --- a/lib/ExpenseRule.jsx +++ b/lib/ExpenseRule.jsx @@ -21,11 +21,7 @@ export default class ExpenseRule { * @return {Object} */ getApplyWhenByField(field) { - return ( - _.find(this.applyWhen, (conditions) => { - return conditions.field === field; - }) || {} - ); + return _.find(this.applyWhen, (conditions) => conditions.field === field) || {}; } /** diff --git a/lib/ExpensiMark.d.ts b/lib/ExpensiMark.d.ts index f431df7e..fad8f1b6 100644 --- a/lib/ExpensiMark.d.ts +++ b/lib/ExpensiMark.d.ts @@ -35,7 +35,7 @@ declare type Rule = { }; declare type ExtrasObject = { - reportIdToName?: Record; + reportIDToName?: Record; accountIDToName?: Record; }; export default class ExpensiMark { diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index 17c84659..7db52a52 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -189,7 +189,10 @@ export default class ExpensiMark { 'gim', ), replacement: (match, g1, g2) => { - if (!Str.isValidMention(match)) { + const phoneNumberRegex = new RegExp(`^${Constants.CONST.REG_EXP.PHONE_PART}$`); + const mention = g2.slice(1); + const mentionWithoutSMSDomain = Str.removeSMSDomain(mention); + if (!Str.isValidMention(match) || (phoneNumberRegex.test(mentionWithoutSMSDomain) && !Str.isValidPhoneNumber(mentionWithoutSMSDomain))) { return match; } const phoneRegex = new RegExp(`^@${Constants.CONST.REG_EXP.PHONE_PART}$`); @@ -421,8 +424,32 @@ export default class ExpensiMark { .trim() .split('\n'); - const prependGreaterSign = (m) => `> ${m}`; - resultString = _.map(resultString, prependGreaterSign).join('\n'); + // Wrap each string in the array with
and
+ // Define a named function to wrap each line with blockquote + function wrapWithBlockquote(line) { + return `
${line}
`; + } + + // Use _.map with the named function + resultString = _.map(resultString, wrapWithBlockquote); + + function processString(m) { + // Recursive function to replace nested
with ">" + function replaceBlockquotes(text) { + let modifiedText = text; + let depth; + do { + depth = (modifiedText.match(/
/gi) || []).length; + modifiedText = modifiedText.replace(/
/gi, ''); + modifiedText = modifiedText.replace(/<\/blockquote>/gi, ''); + } while (/
/i.test(modifiedText)); + return `${'>'.repeat(depth)} ${modifiedText}`; + } + return replaceBlockquotes(m); + } + + resultString = _.map(resultString, processString).join('\n'); + // We want to keep
tag here and let method replaceBlockElementWithNewLine to handle the line break later return `
${resultString}
`; }, @@ -463,7 +490,7 @@ export default class ExpensiMark { name: 'reportMentions', regex: //gi, replacement: (match, g1, offset, string, extras) => { - const reportToNameMap = extras.reportIdToName; + const reportToNameMap = extras.reportIDToName; if (!reportToNameMap || !reportToNameMap[g1]) { Log.alert('[ExpensiMark] Missing report name', {reportID: g1}); return '#Hidden'; @@ -474,15 +501,18 @@ export default class ExpensiMark { }, { name: 'userMention', - regex: //gi, - replacement: (match, g1, offset, string, extras) => { - const accountToNameMap = extras.accountIdToName; - if (!accountToNameMap || !accountToNameMap[g1]) { - Log.alert('[ExpensiMark] Missing account name', {accountID: g1}); - return '@Hidden'; - } + regex: /(?:)|(?:(.*?)<\/mention-user>)/gi, + replacement: (match, g1, g2, offset, string, extras) => { + if (g1) { + const accountToNameMap = extras.accountIDToName; + if (!accountToNameMap || !accountToNameMap[g1]) { + Log.alert('[ExpensiMark] Missing account name', {accountID: g1}); + return '@Hidden'; + } - return `@${extras.accountIdToName[g1]}`; + return `@${extras.accountIDToName[g1]}`; + } + return Str.removeSMSDomain(g2); }, }, ]; @@ -532,7 +562,7 @@ export default class ExpensiMark { name: 'reportMentions', regex: //gi, replacement: (match, g1, offset, string, extras) => { - const reportToNameMap = extras.reportIdToName; + const reportToNameMap = extras.reportIDToName; if (!reportToNameMap || !reportToNameMap[g1]) { Log.alert('[ExpensiMark] Missing report name', {reportID: g1}); return '#Hidden'; @@ -545,13 +575,12 @@ export default class ExpensiMark { name: 'userMention', regex: //gi, replacement: (match, g1, offset, string, extras) => { - const accountToNameMap = extras.accountIdToName; + const accountToNameMap = extras.accountIDToName; if (!accountToNameMap || !accountToNameMap[g1]) { Log.alert('[ExpensiMark] Missing account name', {accountID: g1}); return '@Hidden'; } - - return `@${extras.accountIdToName[g1]}`; + return `@${extras.accountIDToName[g1]}`; }, }, { diff --git a/lib/Log.jsx b/lib/Log.jsx index ba0ca1b1..bd55a1f9 100644 --- a/lib/Log.jsx +++ b/lib/Log.jsx @@ -1,7 +1,9 @@ +/* eslint-disable no-console */ import _ from 'underscore'; import API from './API'; import Network from './Network'; import Logger from './Logger'; +import {isWindowAvailable} from './utils'; /** * Network interface for logger. @@ -22,7 +24,7 @@ function serverLoggingCallback(logger, params) { * @param {String} message */ function clientLoggingCallback(message) { - if (typeof window.g_printableReport !== 'undefined' && window.g_printableReport === true) { + if (isWindowAvailable() && typeof window.g_printableReport !== 'undefined' && window.g_printableReport === true) { return; } @@ -34,5 +36,5 @@ function clientLoggingCallback(message) { export default new Logger({ serverLoggingCallback, clientLoggingCallback, - isDebug: window.DEBUG, + isDebug: isWindowAvailable() ? window.DEBUG : false, }); diff --git a/lib/Logger.d.ts b/lib/Logger.d.ts deleted file mode 100644 index f2b6995e..00000000 --- a/lib/Logger.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -declare type Parameters = string | Record | Array>; -declare type ServerLoggingCallbackOptions = {api_setCookie: boolean; logPacket: string}; -declare type ServerLoggingCallback = (logger: Logger, options: ServerLoggingCallbackOptions) => Promise<{requestID: string}> | undefined; -declare type ClientLoggingCallBack = (message: string) => void; -declare type LogLine = {message: string; parameters: Parameters; onlyFlushWithOthers: boolean; timestamp: Date}; -export default class Logger { - logLines: LogLine[]; - serverLoggingCallback: ServerLoggingCallback; - clientLoggingCallback: ClientLoggingCallBack; - isDebug: boolean; - constructor({serverLoggingCallback, isDebug, clientLoggingCallback}: {serverLoggingCallback: ServerLoggingCallback; isDebug: boolean; clientLoggingCallback: ClientLoggingCallBack}); - /** - * Ask the server to write the log message - */ - logToServer(): void; - /** - * Add a message to the list - * @param message - * @param parameters The parameters associated with the message - * @param forceFlushToServer Should we force flushing all logs to server? - * @param onlyFlushWithOthers A request will never be sent to the server if all loglines have this set to true - */ - add(message: string, parameters: Parameters, forceFlushToServer: boolean, onlyFlushWithOthers?: boolean): void; - /** - * Caches an informational message locally, to be sent to the server if - * needed later. - * - * @param message The message to log. - * @param sendNow if true, the message will be sent right away. - * @param parameters The parameters to send along with the message - * @param onlyFlushWithOthers A request will never be sent to the server if all loglines have this set to true - */ - info(message: string, sendNow?: boolean, parameters?: Parameters, onlyFlushWithOthers?: boolean): void; - /** - * Logs an alert. - * - * @param message The message to alert. - * @param parameters The parameters to send along with the message - * @param includeStackTrace Must be disabled for testing - */ - alert(message: string, parameters?: Parameters, includeStackTrace?: boolean): void; - /** - * Logs a warn. - * - * @param message The message to warn. - * @param parameters The parameters to send along with the message - */ - warn(message: string, parameters?: Parameters): void; - /** - * Logs a hmmm. - * - * @param message The message to hmmm. - * @param parameters The parameters to send along with the message - */ - hmmm(message: string, parameters?: Parameters): void; - /** - * Logs a message in the browser console. - * - * @param message The message to log. - */ - client(message: string): void; -} -export {}; diff --git a/lib/Logger.jsx b/lib/Logger.ts similarity index 51% rename from lib/Logger.jsx rename to lib/Logger.ts index d1e8180b..90d70b9a 100644 --- a/lib/Logger.jsx +++ b/lib/Logger.ts @@ -1,8 +1,22 @@ -import _ from 'underscore'; +type Parameters = string | Record | Array> | Error; +type ServerLoggingCallbackOptions = {api_setCookie: boolean; logPacket: string}; +type ServerLoggingCallback = (logger: Logger, options: ServerLoggingCallbackOptions) => Promise<{requestID: string}> | undefined; +type ClientLoggingCallBack = (message: string) => void; +type LogLine = {message: string; parameters: Parameters; onlyFlushWithOthers?: boolean; timestamp: Date}; +type LoggerOptions = {serverLoggingCallback: ServerLoggingCallback; isDebug: boolean; clientLoggingCallback: ClientLoggingCallBack}; const MAX_LOG_LINES_BEFORE_FLUSH = 50; + export default class Logger { - constructor({serverLoggingCallback, isDebug, clientLoggingCallback}) { + logLines: LogLine[]; + + serverLoggingCallback: ServerLoggingCallback; + + clientLoggingCallback: ClientLoggingCallBack; + + isDebug: boolean; + + constructor({serverLoggingCallback, isDebug, clientLoggingCallback}: LoggerOptions) { // An array of log lines that limits itself to a certain number of entries (deleting the oldest) this.logLines = []; this.serverLoggingCallback = serverLoggingCallback; @@ -10,26 +24,25 @@ export default class Logger { this.isDebug = isDebug; // Public Methods - return { - info: this.info.bind(this), - alert: this.alert.bind(this), - warn: this.warn.bind(this), - hmmm: this.hmmm.bind(this), - client: this.client.bind(this), - }; + this.info = this.info.bind(this); + this.alert = this.alert.bind(this); + this.warn = this.warn.bind(this); + this.hmmm = this.hmmm.bind(this); + this.client = this.client.bind(this); } /** * Ask the server to write the log message */ - logToServer() { + logToServer(): void { // We do not want to call the server with an empty list or if all the lines has onlyFlushWithOthers=true - if (!this.logLines.length || _.all(this.logLines, (l) => l.onlyFlushWithOthers)) { + if (!this.logLines.length || this.logLines?.every((l) => l.onlyFlushWithOthers)) { return; } // We don't care about log setting web cookies so let's define it as false - const linesToLog = _.map(this.logLines, (l) => { + const linesToLog = this.logLines?.map((l) => { + // eslint-disable-next-line no-param-reassign delete l.onlyFlushWithOthers; return l; }); @@ -38,6 +51,7 @@ export default class Logger { if (!promise) { return; } + // eslint-disable-next-line rulesdir/prefer-early-return promise.then((response) => { if (response.requestID) { this.info('Previous log requestID', false, {requestID: response.requestID}, true); @@ -47,12 +61,11 @@ export default class Logger { /** * Add a message to the list - * @param {String} message - * @param {Object|String} parameters The parameters associated with the message - * @param {Boolean} forceFlushToServer Should we force flushing all logs to server? - * @param {Boolean} onlyFlushWithOthers A request will never be sent to the server if all loglines have this set to true + * @param parameters The parameters associated with the message + * @param forceFlushToServer Should we force flushing all logs to server? + * @param onlyFlushWithOthers A request will never be sent to the server if all loglines have this set to true */ - add(message, parameters, forceFlushToServer, onlyFlushWithOthers = false) { + add(message: string, parameters: Parameters, forceFlushToServer: boolean, onlyFlushWithOthers = false) { const length = this.logLines.push({ message, parameters, @@ -74,12 +87,12 @@ export default class Logger { * Caches an informational message locally, to be sent to the server if * needed later. * - * @param {String} message The message to log. - * @param {Boolean} sendNow if true, the message will be sent right away. - * @param {Object|String} parameters The parameters to send along with the message - * @param {Boolean} onlyFlushWithOthers A request will never be sent to the server if all loglines have this set to true + * @param message The message to log. + * @param sendNow if true, the message will be sent right away. + * @param parameters The parameters to send along with the message + * @param onlyFlushWithOthers A request will never be sent to the server if all loglines have this set to true */ - info(message, sendNow = false, parameters = '', onlyFlushWithOthers = false) { + info(message: string, sendNow = false, parameters: Parameters = '', onlyFlushWithOthers = false) { const msg = `[info] ${message}`; this.add(msg, parameters, sendNow, onlyFlushWithOthers); } @@ -87,15 +100,15 @@ export default class Logger { /** * Logs an alert. * - * @param {String} message The message to alert. - * @param {Object|String} parameters The parameters to send along with the message - * @param {Boolean} includeStackTrace Must be disabled for testing + * @param message The message to alert. + * @param parameters The parameters to send along with the message + * @param includeStackTrace Must be disabled for testing */ - alert(message, parameters = {}, includeStackTrace = true) { + alert(message: string, parameters: Parameters = {}, includeStackTrace = true) { const msg = `[alrt] ${message}`; const params = parameters; - if (includeStackTrace) { + if (includeStackTrace && typeof params === 'object' && !Array.isArray(params)) { params.stack = JSON.stringify(new Error().stack); } @@ -108,7 +121,7 @@ export default class Logger { * @param {String} message The message to warn. * @param {Object|String} parameters The parameters to send along with the message */ - warn(message, parameters = '') { + warn(message: string, parameters: Parameters = '') { const msg = `[warn] ${message}`; this.add(msg, parameters, true); } @@ -116,10 +129,10 @@ export default class Logger { /** * Logs a hmmm. * - * @param {String} message The message to hmmm. - * @param {Object|String} parameters The parameters to send along with the message + * @param message The message to hmmm. + * @param parameters The parameters to send along with the message */ - hmmm(message, parameters = '') { + hmmm(message: string, parameters: Parameters = '') { const msg = `[hmmm] ${message}`; this.add(msg, parameters, false); } @@ -127,9 +140,9 @@ export default class Logger { /** * Logs a message in the browser console. * - * @param {String} message The message to log. + * @param message The message to log. */ - client(message) { + client(message: string) { if (!this.clientLoggingCallback) { return; } diff --git a/lib/Network.jsx b/lib/Network.jsx index ee388da0..85c6e432 100644 --- a/lib/Network.jsx +++ b/lib/Network.jsx @@ -1,5 +1,6 @@ import $ from 'jquery'; import _ from 'underscore'; +import {isWindowAvailable} from './utils'; /** * Adds our API command to the URL so the API call is more easily identified in the @@ -40,9 +41,11 @@ export default function Network(endpoint) { } // Attach a listener to the event indicating that we're leaving a page - window.onbeforeunload = () => { - isNavigatingAway = true; - }; + if (isWindowAvailable()) { + window.onbeforeunload = () => { + isNavigatingAway = true; + }; + } return { /** @@ -64,6 +67,7 @@ export default function Network(endpoint) { if (isNewURLFormat) { // Remove command from parameters and use it in the URL const command = parameters.command; + // eslint-disable-next-line no-param-reassign delete parameters.command; newURL = `${endpoint}${command}`; } diff --git a/lib/PubSub.jsx b/lib/PubSub.jsx index 31014c4a..28fc6ea7 100644 --- a/lib/PubSub.jsx +++ b/lib/PubSub.jsx @@ -1,6 +1,7 @@ import _ from 'underscore'; import has from 'lodash/has'; import Log from './Log'; +import {isWindowAvailable} from './utils'; /** * PubSub @@ -125,4 +126,4 @@ const PubSubModule = { }, }; -export default window !== undefined && window.PubSub ? window.PubSub : PubSubModule; +export default isWindowAvailable() && window.PubSub ? window.PubSub : PubSubModule; diff --git a/lib/ReportHistoryStore.jsx b/lib/ReportHistoryStore.jsx index 6cabca88..c8acae97 100644 --- a/lib/ReportHistoryStore.jsx +++ b/lib/ReportHistoryStore.jsx @@ -263,9 +263,6 @@ export default class ReportHistoryStore { delete this.cache[reportID]; } - // We'll poll the API for the un-cached history - const cachedHistory = this.cache[reportID] || []; - this.API.Report_GetHistory({ reportID, }) diff --git a/lib/Templates.jsx b/lib/Templates.jsx index 5f2c3878..e5e21fe8 100644 --- a/lib/Templates.jsx +++ b/lib/Templates.jsx @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import _ from 'underscore'; import $ from 'jquery'; @@ -67,6 +68,7 @@ export default (function () { get(data = {}) { // Add the "template" object to the parameter to allow nested templates const dataToCompile = {...data}; + // eslint-disable-next-line no-undef dataToCompile.nestedTemplate = Templates.get; if (!this.compiled) { this.compiled = _.template($(`#${this.id}`).html()); @@ -119,22 +121,18 @@ export default (function () { * @return {String} */ get(templatePath, data = {}) { - try { - const template = getTemplate(templatePath); - if (_.isUndefined(template)) { - throw Error(`Template '${templatePath}' is not defined`); - } - - // Check for the absense of get which means someone is likely using - // the templating engine wrong and trying to access a template namespace - if (!{}.propertyIsEnumerable.call(template, 'get')) { - throw Error(`'${templatePath}' is not a valid template path`); - } + const template = getTemplate(templatePath); + if (_.isUndefined(template)) { + throw Error(`Template '${templatePath}' is not defined`); + } - return template.get(data); - } catch (err) { - throw err; + // Check for the absense of get which means someone is likely using + // the templating engine wrong and trying to access a template namespace + if (!{}.propertyIsEnumerable.call(template, 'get')) { + throw Error(`'${templatePath}' is not a valid template path`); } + + return template.get(data); }, /** @@ -151,6 +149,7 @@ export default (function () { */ init() { // Read the DOM to find all the templates, and make them available to the code + // eslint-disable-next-line rulesdir/prefer-underscore-method $('.js_template').each((__, $el) => { const namespaceElements = $el.id.split('_'); const id = namespaceElements.pop(); diff --git a/lib/components/form/element/onOffSwitch.jsx b/lib/components/form/element/onOffSwitch.jsx index b3a4da29..72780595 100644 --- a/lib/components/form/element/onOffSwitch.jsx +++ b/lib/components/form/element/onOffSwitch.jsx @@ -19,6 +19,7 @@ const propTypes = { labelOnRight: PropTypes.bool, // Classes of the label + // eslint-disable-next-line react/forbid-prop-types labelClasses: PropTypes.any, // True if the switch is on @@ -176,6 +177,7 @@ class OnOffSwitch extends Component { descriptionElm = (
); diff --git a/lib/fastMerge.d.ts b/lib/fastMerge.d.ts deleted file mode 100644 index 4355619f..00000000 --- a/lib/fastMerge.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true - * - * We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk. - * On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values. - * To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations. - */ -declare function fastMerge(target: T, source: T, shouldRemoveNullObjectValues: boolean): T; - -export default fastMerge; diff --git a/lib/fastMerge.js b/lib/fastMerge.js deleted file mode 100644 index 57352cff..00000000 --- a/lib/fastMerge.js +++ /dev/null @@ -1,88 +0,0 @@ -import _ from 'underscore'; - -// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1 - -/** - * @param {mixed} val - * @returns {boolean} - */ -function isMergeableObject(val) { - const nonNullObject = val != null ? typeof val === 'object' : false; - return nonNullObject && Object.prototype.toString.call(val) !== '[object RegExp]' && Object.prototype.toString.call(val) !== '[object Date]' && !_.isArray(val); -} - -/** - * @param {Object} target - * @param {Object} source - * @param {Boolean} shouldRemoveNullObjectValues - * @returns {Object} - */ -function mergeObject(target, source, shouldRemoveNullObjectValues = true) { - const destination = {}; - if (isMergeableObject(target)) { - // lodash adds a small overhead so we don't use it here - const targetKeys = _.keys(target); - for (let i = 0; i < targetKeys.length; ++i) { - const key = targetKeys[i]; - - // If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object - const isSourceOrTargetNull = target[key] === null || source[key] === null; - const shouldOmitSourceKey = shouldRemoveNullObjectValues && isSourceOrTargetNull; - - if (!shouldOmitSourceKey) { - destination[key] = target[key]; - } - } - } - - // lodash adds a small overhead so we don't use it here - const sourceKeys = _.keys(source); - for (let i = 0; i < sourceKeys.length; ++i) { - const key = sourceKeys[i]; - - // If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object - const shouldOmitSourceKey = shouldRemoveNullObjectValues && source[key] === null; - - // If we pass undefined as the updated value for a key, we want to generally ignore it - const isSourceKeyUndefined = source[key] === undefined; - - if (!isSourceKeyUndefined && !shouldOmitSourceKey) { - const isSourceKeyMergable = isMergeableObject(source[key]); - - if (isSourceKeyMergable && target[key]) { - if (!shouldRemoveNullObjectValues || isSourceKeyMergable) { - // eslint-disable-next-line no-use-before-define - destination[key] = fastMerge(target[key], source[key], shouldRemoveNullObjectValues); - } - } else if (!shouldRemoveNullObjectValues || source[key] !== null) { - destination[key] = source[key]; - } - } - } - - return destination; -} - -/** - * Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true - * - * We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk. - * On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values. - * To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations. - * - * @param {Object|Array} target - * @param {Object|Array} source - * @param {Boolean} shouldRemoveNullObjectValues - * @returns {Object|Array} - */ -function fastMerge(target, source, shouldRemoveNullObjectValues = true) { - // We have to ignore arrays and nullish values here, - // otherwise "mergeObject" will throw an error, - // because it expects an object as "source" - if (_.isArray(source) || source === null || source === undefined) { - return source; - } - return mergeObject(target, source, shouldRemoveNullObjectValues); -} - -export default fastMerge; diff --git a/lib/fastMerge.ts b/lib/fastMerge.ts new file mode 100644 index 00000000..a7ce03bd --- /dev/null +++ b/lib/fastMerge.ts @@ -0,0 +1,88 @@ +/* eslint-disable @typescript-eslint/prefer-for-of */ + +// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1 + +/** + * Checks whether the given value can be merged. It has to be an object, but not an array, RegExp or Date. + */ +function isMergeableObject(value: unknown): value is Record { + const nonNullObject = value != null ? typeof value === 'object' : false; + return nonNullObject && Object.prototype.toString.call(value) !== '[object RegExp]' && Object.prototype.toString.call(value) !== '[object Date]' && !Array.isArray(value); +} + +/** + * Merges the source object into the target object. + * @param target - The target object. + * @param source - The source object. + * @param shouldRemoveNestedNulls - If true, null object values will be removed. + * @returns - The merged object. + */ +function mergeObject>(target: TObject, source: TObject, shouldRemoveNullObjectValues = true): TObject { + const destination: Record = {}; + + if (isMergeableObject(target)) { + // lodash adds a small overhead so we don't use it here + const targetKeys = Object.keys(target); + for (let i = 0; i < targetKeys.length; ++i) { + const key = targetKeys[i]; + const sourceValue = source?.[key]; + const targetValue = target?.[key]; + + // If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object + const isSourceOrTargetNull = targetValue === null || sourceValue === null; + const shouldOmitSourceKey = shouldRemoveNullObjectValues && isSourceOrTargetNull; + + if (!shouldOmitSourceKey) { + destination[key] = targetValue; + } + } + } + + // lodash adds a small overhead so we don't use it here + const sourceKeys = Object.keys(source); + for (let i = 0; i < sourceKeys.length; ++i) { + const key = sourceKeys[i]; + const sourceValue = source?.[key]; + const targetValue = target?.[key]; + + // If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object + const shouldOmitSourceKey = shouldRemoveNullObjectValues && sourceValue === null; + + // If we pass undefined as the updated value for a key, we want to generally ignore it + const isSourceKeyUndefined = sourceValue === undefined; + + if (!isSourceKeyUndefined && !shouldOmitSourceKey) { + const isSourceKeyMergable = isMergeableObject(sourceValue); + + if (isSourceKeyMergable && targetValue) { + if (!shouldRemoveNullObjectValues || isSourceKeyMergable) { + // eslint-disable-next-line no-use-before-define + destination[key] = fastMerge(targetValue as TObject, sourceValue, shouldRemoveNullObjectValues); + } + } else if (!shouldRemoveNullObjectValues || sourceValue !== null) { + destination[key] = sourceValue; + } + } + } + + return destination as TObject; +} + +/** + * Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true + * + * We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk. + * On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values. + * To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations. + */ +function fastMerge(target: TObject, source: TObject, shouldRemoveNullObjectValues = true): TObject { + // We have to ignore arrays and nullish values here, + // otherwise "mergeObject" will throw an error, + // because it expects an object as "source" + if (Array.isArray(source) || source === null || source === undefined) { + return source; + } + return mergeObject(target as Record, source as Record, shouldRemoveNullObjectValues) as TObject; +} + +export default fastMerge; diff --git a/lib/index.ts b/lib/index.ts new file mode 100644 index 00000000..dc9c5b20 --- /dev/null +++ b/lib/index.ts @@ -0,0 +1,21 @@ +// eslint-disable-next-line rulesdir/no-api-in-views +export {default as API} from './API'; +export {default as APIDeferred} from './APIDeferred'; +export {default as BrowserDetect} from './BrowserDetect'; +export {g_cloudFront, g_cloudFrontImg, CONST, UI, PUBLIC_DOMAINS} from './CONST'; + +export {default as Cookie} from './Cookie'; +export {default as CredentialsWrapper, LOGIN_PARTNER_DETAILS} from './CredentialsWrapper'; +export * as Device from './Device'; +export {default as ExpensiMark} from './ExpensiMark'; +export {default as Logger} from './Logger'; +export {default as Network} from './Network'; +export {default as Num} from './Num'; +export {default as PageEvent} from './PageEvent'; +export {default as PubSub} from './PubSub'; +export {default as ReportHistoryStore} from './ReportHistoryStore'; +export {default as Templates} from './Templates'; +export * as Url from './Url'; +export {default as fastMerge} from './fastMerge'; +export {default as Str} from './str'; +export {default as TLD_REGEX} from './tlds'; diff --git a/lib/mixins/PubSub.jsx b/lib/mixins/PubSub.jsx index 86855a5d..8be94395 100644 --- a/lib/mixins/PubSub.jsx +++ b/lib/mixins/PubSub.jsx @@ -1,7 +1,8 @@ import _ from 'underscore'; import PubSubModule from '../PubSub'; +import {isWindowAvailable} from '../utils'; -const PubSub = window.PubSub || PubSubModule; +const PubSub = (isWindowAvailable() && window.PubSub) || PubSubModule; /** * This mixin sets up automatic PubSub bindings which will be removed when diff --git a/lib/mixins/extraClasses.js b/lib/mixins/extraClasses.js index 5588bc6b..13acbfb1 100644 --- a/lib/mixins/extraClasses.js +++ b/lib/mixins/extraClasses.js @@ -22,9 +22,12 @@ * return
; * } */ + +import {isWindowAvailable} from '../utils'; + export default { propTypes: { - extraClasses: window.PropTypes.oneOfType([window.PropTypes.string, window.PropTypes.array, window.PropTypes.object]), + extraClasses: isWindowAvailable() && window.PropTypes.oneOfType([window.PropTypes.string, window.PropTypes.array, window.PropTypes.object]), }, UNSAFE_componentWillReceiveProps(nextProps) { diff --git a/lib/str.d.ts b/lib/str.d.ts index d703d8bf..f48bc35a 100644 --- a/lib/str.d.ts +++ b/lib/str.d.ts @@ -478,9 +478,14 @@ declare const Str: { /** * Check for whether a phone number is valid. * @param phone - * @deprecated use isValidE164Phone to validate E.164 phone numbers or isValidPhoneFormat to validate phone numbers in general + * @deprecated use isValidE164Phone to validate E.164 phone numbers, isValidPhoneFormat to validate phone number format, or isValidPhoneNumber to validate phone numbers in general */ isValidPhone(phone: string): boolean; + /** + * Check for whether a phone number is valid. + * @param phone + */ + isValidPhoneNumber(phone: string): boolean; /** * Check for whether a phone number is valid according to E.164 standard. * @param phone diff --git a/lib/str.js b/lib/str.js index f9978b4e..12571587 100644 --- a/lib/str.js +++ b/lib/str.js @@ -1,5 +1,6 @@ /* eslint-disable no-control-regex */ import _ from 'underscore'; +import {parsePhoneNumber} from 'awesome-phonenumber'; import * as HtmlEntities from 'html-entities'; import * as Constants from './CONST'; import * as UrlPatterns from './Url'; @@ -930,6 +931,16 @@ const Str = { return Constants.CONST.SMS.E164_REGEX.test(phone); }, + /** + * Check for whether a phone number is valid. + * @param {String} phone + * + * @return {bool} + */ + isValidPhoneNumber(phone) { + return parsePhoneNumber(phone).possible; + }, + /** * Check for whether a phone number is valid according to E.164 standard. * @param {String} phone diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 00000000..05f79512 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,11 @@ +/** Checks if the `window` global object is available. */ +function isWindowAvailable(): boolean { + return typeof window !== 'undefined'; +} + +/** Checks if the `navigator` global object is available. */ +function isNavigatorAvailable(): boolean { + return typeof navigator !== 'undefined'; +} + +export {isWindowAvailable, isNavigatorAvailable}; diff --git a/package-lock.json b/package-lock.json index 0076e0ef..19a4bcde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,18 @@ { "name": "expensify-common", - "version": "1.0.0", + "version": "2.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "expensify-common", - "version": "1.0.0", + "version": "2.0.5", "license": "MIT", "dependencies": { + "awesome-phonenumber": "^5.4.0", "classnames": "2.5.0", "clipboard": "2.0.11", + "eslint-plugin-you-dont-need-lodash-underscore": "^6.14.0", "html-entities": "^2.5.2", "jquery": "3.6.0", "localforage": "^1.10.0", @@ -25,20 +27,28 @@ }, "devDependencies": { "@babel/preset-env": "^7.24.4", + "@babel/preset-typescript": "^7.24.1", "@lwc/eslint-plugin-lwc": "^1.8.0", + "@types/jest": "^29.5.12", + "@types/react-dom": "^18.3.0", + "@types/ua-parser-js": "^0.7.39", + "@typescript-eslint/eslint-plugin": "^7.9.0", + "@typescript-eslint/parser": "^7.9.0", "babel-jest": "^29.0.0", "babelify": "10.0.0", - "eslint": "^7.15.0", + "eslint": "^8.57.0", "eslint-config-expensify": "^2.0.48", "eslint-config-prettier": "^8.10.0", "eslint-plugin-jest": "^24.7.0", + "eslint-plugin-prettier": "^5.1.3", "grunt": "1.6.1", "grunt-chokidar": "1.0.2", "grunt-eslint": "25.0.0", "jest": "^29.0.0", "jest-environment-jsdom": "^29.7.0", "jit-grunt": "^0.10.0", - "prettier": "^3.2.5" + "prettier": "^3.2.5", + "typescript": "^5.4.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -243,19 +253,19 @@ "dev": true }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", - "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.5.tgz", + "integrity": "sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.24.5", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-split-export-declaration": "^7.24.5", "semver": "^6.3.1" }, "engines": { @@ -351,12 +361,12 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.5.tgz", + "integrity": "sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -406,9 +416,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -473,30 +483,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -771,12 +781,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -888,12 +898,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1626,6 +1636,24 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.5.tgz", + "integrity": "sha512-E0VWu/hk83BIFUWnsKZ4D81KXjN5L3MobvevOHErASk9IPwKHOkTgvqzvNo1yP/ePJWqqK2SpUR5z+KQbl6NVw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.5", + "@babel/helper-plugin-utils": "^7.24.5", + "@babel/plugin-syntax-typescript": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", @@ -1807,6 +1835,25 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", + "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-syntax-jsx": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-typescript": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", @@ -1861,13 +1908,13 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1951,18 +1998,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.0.0.tgz", @@ -2808,18 +2843,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lwc/eslint-plugin-lwc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -2865,6 +2888,18 @@ "node": ">= 8" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2972,6 +3007,16 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", @@ -3004,6 +3049,31 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz", + "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -3016,6 +3086,12 @@ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true }, + "node_modules/@types/ua-parser-js": { + "version": "0.7.39", + "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", + "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3031,6 +3107,107 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", + "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/type-utils": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/experimental-utils": { "version": "4.16.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.16.1.tgz", @@ -3055,52 +3232,81 @@ "eslint": "*" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz", - "integrity": "sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==", + "node_modules/@typescript-eslint/parser": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", + "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/visitor-keys": "4.16.1" + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/types": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.16.1.tgz", - "integrity": "sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" + }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz", - "integrity": "sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/visitor-keys": "4.16.1", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3112,65 +3318,435 @@ } } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz", - "integrity": "sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.16.1", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, "engines": { - "node": ">=0.4.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz", + "integrity": "sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.16.1", + "@typescript-eslint/visitor-keys": "4.16.1" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", + "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.16.1.tgz", + "integrity": "sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz", + "integrity": "sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.16.1", + "@typescript-eslint/visitor-keys": "4.16.1", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz", + "integrity": "sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.16.1", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" } }, "node_modules/acorn-globals/node_modules/acorn": { @@ -3527,15 +4103,6 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", @@ -3563,6 +4130,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/awesome-phonenumber": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/awesome-phonenumber/-/awesome-phonenumber-5.11.0.tgz", + "integrity": "sha512-25GfikMIo6CBQIqvjoewo4uiu5Ai7WqEC8gxesH3LDwCY43oEdkLaT15a+8adC7uWIJCGh+YQiBY5bjmDpoQcg==", + "engines": { + "node": ">=14" + } + }, "node_modules/axe-core": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", @@ -4336,6 +4911,12 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -4890,54 +5471,55 @@ } }, "node_modules/eslint": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz", - "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", - "ajv": "^6.10.0", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5302,18 +5884,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint-config-expensify/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-config-prettier": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", @@ -5533,6 +6103,36 @@ "node": "*" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.34.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", @@ -5645,6 +6245,17 @@ "node": ">=4.0.0" } }, + "node_modules/eslint-plugin-you-dont-need-lodash-underscore": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-you-dont-need-lodash-underscore/-/eslint-plugin-you-dont-need-lodash-underscore-6.14.0.tgz", + "integrity": "sha512-3zkkU/O1agczP7szJGHmisZJS/AknfVl6mb0Zqoc95dvFsdmfK+cbhrn+Ffy0UWB1pgDJwQr7kIO3rPstWs3Dw==", + "dependencies": { + "kebab-case": "^1.0.0" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -5683,24 +6294,67 @@ } }, "node_modules/eslint/node_modules/@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/eslint/node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "node_modules/eslint/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -5718,6 +6372,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5752,13 +6412,70 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" } }, "node_modules/eslint/node_modules/file-entry-cache": { @@ -5773,6 +6490,22 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -5787,13 +6520,25 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -5811,6 +6556,84 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6015,21 +6838,26 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -6447,16 +7275,16 @@ } }, "node_modules/globby": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", - "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -6467,9 +7295,9 @@ } }, "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -9815,6 +10643,11 @@ "node": ">=4.0" } }, + "node_modules/kebab-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.2.tgz", + "integrity": "sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10612,6 +11445,18 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -11246,29 +12091,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11444,27 +12266,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -11525,50 +12326,28 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, "dependencies": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, - "node_modules/table/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, - "node_modules/table/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/table/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -11671,6 +12450,18 @@ "node": ">=12" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -11738,12 +12529,15 @@ } }, "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typed-array-buffer": { @@ -11820,11 +12614,10 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 9e0554ca..c32b1999 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,22 @@ { "name": "expensify-common", - "version": "1.0.0", + "version": "2.0.5", "author": "Expensify, Inc.", "description": "Expensify libraries and components shared across different repos", "homepage": "https://expensify.com", - "main": "index.js", "license": "MIT", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/**/*", + "API.md", + "README.md", + "LICENSE.md" + ], "scripts": { "grunt": "grunt", + "typecheck": "tsc --noEmit", + "build": "tsc -p tsconfig.build.json && cp ./lib/*.d.ts ./dist", "test": "jest", "lint": "eslint lib/", "prettier": "prettier --write lib/", @@ -19,8 +28,10 @@ "url": "git+ssh://git@github.com/Expensify/JS-Libs.git" }, "dependencies": { + "awesome-phonenumber": "^5.4.0", "classnames": "2.5.0", "clipboard": "2.0.11", + "eslint-plugin-you-dont-need-lodash-underscore": "^6.14.0", "html-entities": "^2.5.2", "jquery": "3.6.0", "localforage": "^1.10.0", @@ -35,20 +46,28 @@ }, "devDependencies": { "@babel/preset-env": "^7.24.4", + "@babel/preset-typescript": "^7.24.1", "@lwc/eslint-plugin-lwc": "^1.8.0", + "@types/jest": "^29.5.12", + "@types/react-dom": "^18.3.0", + "@types/ua-parser-js": "^0.7.39", + "@typescript-eslint/eslint-plugin": "^7.9.0", + "@typescript-eslint/parser": "^7.9.0", "babel-jest": "^29.0.0", "babelify": "10.0.0", - "eslint": "^7.15.0", + "eslint": "^8.57.0", "eslint-config-expensify": "^2.0.48", "eslint-config-prettier": "^8.10.0", "eslint-plugin-jest": "^24.7.0", + "eslint-plugin-prettier": "^5.1.3", "grunt": "1.6.1", "grunt-chokidar": "1.0.2", "grunt-eslint": "25.0.0", "jest": "^29.0.0", "jest-environment-jsdom": "^29.7.0", "jit-grunt": "^0.10.0", - "prettier": "^3.2.5" + "prettier": "^3.2.5", + "typescript": "^5.4.5" }, "browserify": { "transform": [ diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..d04f033a --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "include": ["./lib"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..92431b67 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "es2015", + "module": "commonjs", + "types": ["react", "jest", "node"], + "lib": ["esnext"], + "allowJs": true, + "checkJs": false, + "jsx": "react", + "isolatedModules": true, + "strict": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "outDir": "./dist" + }, + "exclude": ["**/node_modules/**/*", "**/dist/**/*"] +}