diff --git a/.eslintignore b/.eslintignore index d19fc7bf..9c2bae7c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ !.github node_modules/ +dist diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..519f5cd6 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,56 @@ +module.exports = { + root: true, + env: { + es2020: true, + browser: true, + node: true, + }, + extends: [ + 'plugin:@typescript-eslint/recommended', + 'eslint:recommended', + 'plugin:import/typescript', + 'plugin:jsx-a11y/recommended', + 'plugin:@typescript-eslint/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + ecmaFeatures: { jsx: true }, + }, + plugins: [ + 'html', + 'node', + '@typescript-eslint', + 'jest', + '@typescript-eslint', + 'simple-import-sort', + ], + rules: { + 'no-param-reassign': 'error', + 'no-use-before-define': 'off', + 'no-underscore-dangle': 'off', + 'no-implicit-coercion': ['error', { allow: ['!!'] }], + 'block-scoped-var': 'error', + 'no-shadow': 'warn', + 'no-unsafe-optional-chaining': ['error', { disallowArithmeticOperators: true }], + 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + 'no-return-assign': 'warn', + 'consistent-return': 'off', + 'no-redeclare': 'error', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + 'import/prefer-default-export': 'off', + 'jsx-a11y/anchor-is-valid': 'warn', + 'jsx-a11y/no-noninteractive-tabindex': 'warn', + 'jsx-a11y/tabindex-no-positive': 'warn', + 'jsx-a11y/click-events-have-key-events': 'warn', + 'jsx-a11y/no-static-element-interactions': 'warn', + 'jsx-a11y/no-noninteractive-element-interactions': 'warn', + // TODO: Re-ctivate rule when we have a custom logger + 'no-console': 'off', + 'no-empty': ['error', { allowEmptyCatch: true }], + '@typescript-eslint/no-explicit-any': 'off', + }, + ignorePatterns: ['**/dist/**', '**/dev/**', '**/coverage/**', '**/temp/**'], +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 01f96c60..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "env": { - "es2020": true, - "browser": true, - "node": true - }, - "extends": [ - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended", - "eslint:recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - "plugin:import/typescript", - "plugin:jsx-a11y/recommended", - "plugin:cypress/recommended", - "prettier", - "plugin:jest/recommended", - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "sourceType": "module", - "ecmaVersion": 2020, - "ecmaFeatures": { "jsx": true } - }, - "plugins": [ - "html", - "prettier", - "react", - "node", - "react-hooks", - "cypress", - "@typescript-eslint", - "jest", - "@typescript-eslint", - "simple-import-sort" - ], - "settings": { - "react": { - "version": "detect" - } - }, - "rules": { - "semi": "off", - "prettier/prettier": "error", - "maxWarnings": "off", - "react/self-closing-comp": [ - "error", - { - "component": true, - "html": true - } - ], - "react/prop-types": "off", - "no-param-reassign": "error", - "no-use-before-define": "off", - "no-underscore-dangle": "off", - "no-implicit-coercion": ["error", { "allow": ["!!"] }], - "block-scoped-var": "error", - "no-shadow": "warn", - "no-unsafe-optional-chaining": ["error", { "disallowArithmeticOperators": true }], - "no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], - "no-return-assign": "warn", - "consistent-return": "off", - "no-redeclare": "error", - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "react/jsx-props-no-spreading": "off", - "import/prefer-default-export": "off", - "jsx-a11y/anchor-is-valid": "warn", - "jsx-a11y/no-noninteractive-tabindex": [ - "warn", - { - "tags": [], - "roles": ["tabpanel"], - "allowExpressionValues": true - } - ], - "jsx-a11y/tabindex-no-positive": "warn", - "jsx-a11y/click-events-have-key-events": "warn", - "jsx-a11y/no-static-element-interactions": "warn", - "jsx-a11y/no-noninteractive-element-interactions": "warn", - "no-console": "warn", - "no-empty": ["error", { "allowEmptyCatch": true }], - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - "react/react-in-jsx-scope": "off", - "react/jsx-uses-react": "off", - "@typescript-eslint/no-explicit-any": "off" - }, - "overrides": [ - { - "files": [".*rc.js", ".*rc.cjs", "*.config.js", "*.config.cjs"], - "env": { - "node": true - } - } - ], - "ignorePatterns": ["dist/**", "dev/**", "coverage/**"], - "globals": { - "chrome": true, - "browser": true - } -} diff --git a/.github/actions/constants.cjs b/.github/actions/constants.cjs index aeb9b973..82bcbe5f 100644 --- a/.github/actions/constants.cjs +++ b/.github/actions/constants.cjs @@ -1,10 +1,10 @@ const BADGE = - 'Badge' -const BROWSERS = ['chrome', 'firefox', 'opera', 'edge'] + 'Badge'; +const BROWSERS = ['chrome', 'firefox', 'opera', 'edge']; const COLORS = { green: '3fb950', red: 'd73a49', -} +}; const TEMPLATE_VARS = { tableBody: '{{ TABLE_BODY }}', sha: '{{ SHA }}', @@ -12,11 +12,11 @@ const TEMPLATE_VARS = { badgeColor: '{{ BADGE_COLOR }}', badgeLabel: '{{ BADGE_LABEL }}', jobLogs: '{{ JOB_LOGS }}', -} +}; module.exports = { BADGE, BROWSERS, COLORS, TEMPLATE_VARS, -} +}; diff --git a/.github/actions/delete-artifacts.cjs b/.github/actions/delete-artifacts.cjs index 216bc033..2a891330 100644 --- a/.github/actions/delete-artifacts.cjs +++ b/.github/actions/delete-artifacts.cjs @@ -1,25 +1,25 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable no-console */ -const { BROWSERS } = require('./constants.cjs') +const { BROWSERS } = require('./constants.cjs'); async function getBrowserArfifacts({ github, owner, repo, name }) { - const artifacts = [] + const artifacts = []; const result = await github.rest.actions.listArtifactsForRepo({ owner, repo, name, - }) + }); for (let i = 0; i < result.data.total_count; i++) { - artifacts.push(result.data.artifacts[i].id) + artifacts.push(result.data.artifacts[i].id); } - return artifacts + return artifacts; } async function getPRArtifacts({ github, owner, repo, prNumber }) { - const promises = [] - const artifacts = [] + const promises = []; + const artifacts = []; BROWSERS.forEach(browser => promises.push( @@ -30,27 +30,27 @@ async function getPRArtifacts({ github, owner, repo, prNumber }) { name: `${prNumber}-${browser}`, }), ), - ) + ); - const data = await Promise.all(promises) + const data = await Promise.all(promises); for (let i = 0; i < data.length; i++) { - artifacts.push.apply(artifacts, data[i]) + artifacts.push.apply(artifacts, data[i]); } - return artifacts + return artifacts; } module.exports = async ({ github, context, core }) => { if (context.payload.action !== 'closed') { - core.setFailed('This action only works on closed PRs.') + core.setFailed('This action only works on closed PRs.'); } - const { owner, repo } = context.repo - const prNumber = context.payload.number - const promises = [] + const { owner, repo } = context.repo; + const prNumber = context.payload.number; + const promises = []; - const artifacts = await getPRArtifacts({ github, owner, repo, prNumber }) + const artifacts = await getPRArtifacts({ github, owner, repo, prNumber }); for (let i = 0; i < artifacts.length; i++) { promises.push( @@ -59,9 +59,9 @@ module.exports = async ({ github, context, core }) => { repo, artifact_id: artifacts[i], }), - ) + ); } - await Promise.all(promises) - console.log(`Deleted ${artifacts.length} artifacts for PR #${prNumber}.`) -} + await Promise.all(promises); + console.log(`Deleted ${artifacts.length} artifacts for PR #${prNumber}.`); +}; diff --git a/.github/actions/get-workflow-artifacts.cjs b/.github/actions/get-workflow-artifacts.cjs index e6ada883..2db0e7e3 100644 --- a/.github/actions/get-workflow-artifacts.cjs +++ b/.github/actions/get-workflow-artifacts.cjs @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable no-console */ -const fs = require('node:fs/promises') -const { COLORS, TEMPLATE_VARS, BADGE } = require('./constants.cjs') +const fs = require('node:fs/promises'); +const { COLORS, TEMPLATE_VARS, BADGE } = require('./constants.cjs'); const ARTIFACTS_DATA = { chrome: { @@ -24,73 +24,73 @@ const ARTIFACTS_DATA = { url: null, size: null, }, -} +}; function getBadge(conclusion, badgeColor, badgeLabel) { return BADGE.replace(TEMPLATE_VARS.conslusion, conclusion) .replace(TEMPLATE_VARS.badgeColor, badgeColor) - .replace(TEMPLATE_VARS.badgeLabel, badgeLabel) + .replace(TEMPLATE_VARS.badgeLabel, badgeLabel); } function formatBytes(bytes, decimals = 2) { - if (!Number(bytes)) return '0B' - const k = 1024 - const dm = decimals < 0 ? 0 : decimals - const sizes = ['B', 'KB', 'MB', 'GB'] - const i = Math.floor(Math.log(bytes) / Math.log(k)) - return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}` + if (!Number(bytes)) return '0B'; + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`; } module.exports = async ({ github, context, core }) => { - const { owner, repo } = context.repo - const baseUrl = context.payload.repository.html_url - const suiteId = context.payload.workflow_run.check_suite_id - const runId = context.payload.workflow_run.id - const conclusion = context.payload.workflow_run.conclusion - const sha = context.payload.workflow_run.pull_requests[0].head.sha - const prNumber = context.payload.workflow_run.pull_requests[0].number - const jobLogsUrl = `${baseUrl}/actions/runs/${context.payload.workflow_run.id}` - const template = await fs.readFile('./.github/actions/templates/build-status.md', 'utf8') - const tableRows = [] + const { owner, repo } = context.repo; + const baseUrl = context.payload.repository.html_url; + const suiteId = context.payload.workflow_run.check_suite_id; + const runId = context.payload.workflow_run.id; + const conclusion = context.payload.workflow_run.conclusion; + const sha = context.payload.workflow_run.pull_requests[0].head.sha; + const prNumber = context.payload.workflow_run.pull_requests[0].number; + const jobLogsUrl = `${baseUrl}/actions/runs/${context.payload.workflow_run.id}`; + const template = await fs.readFile('./.github/actions/templates/build-status.md', 'utf8'); + const tableRows = []; - core.setOutput('conclusion', conclusion) + core.setOutput('conclusion', conclusion); if (conclusion === 'cancelled') { - return + return; } const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner, repo, run_id: runId, - }) + }); artifacts.data.artifacts.forEach(artifact => { - const [, key] = artifact.name.split('-') - ARTIFACTS_DATA[key].url = `${baseUrl}/suites/${suiteId}/artifacts/${artifact.id}` - ARTIFACTS_DATA[key].size = formatBytes(artifact.size_in_bytes) - }) + const [, key] = artifact.name.split('-'); + ARTIFACTS_DATA[key].url = `${baseUrl}/suites/${suiteId}/artifacts/${artifact.id}`; + ARTIFACTS_DATA[key].size = formatBytes(artifact.size_in_bytes); + }); Object.keys(ARTIFACTS_DATA).forEach(k => { - const { name, url, size } = ARTIFACTS_DATA[k] + const { name, url, size } = ARTIFACTS_DATA[k]; if (url === null && size === null) { - const badgeUrl = getBadge('failure', COLORS.red, name) - tableRows.push(`${badgeUrl}N/A`) + const badgeUrl = getBadge('failure', COLORS.red, name); + tableRows.push(`${badgeUrl}N/A`); } else { - const badgeUrl = getBadge('success', COLORS.green, `${name} (${size})`) + const badgeUrl = getBadge('success', COLORS.green, `${name} (${size})`); tableRows.push( `${badgeUrl}Download`, - ) + ); } - }) + }); - const tableBody = tableRows.join('') + const tableBody = tableRows.join(''); const commentBody = template .replace(TEMPLATE_VARS.conslusion, conclusion) .replace(TEMPLATE_VARS.sha, sha) .replace(TEMPLATE_VARS.jobLogs, `Run #${runId}`) - .replace(TEMPLATE_VARS.tableBody, tableBody) + .replace(TEMPLATE_VARS.tableBody, tableBody); - core.setOutput('comment_body', commentBody) - core.setOutput('pr_number', prNumber) -} + core.setOutput('comment_body', commentBody); + core.setOutput('pr_number', prNumber); +}; diff --git a/.github/labeler.yml b/.github/labeler.yml index 330791c3..f7f3f897 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,7 +1,12 @@ +'area: server': + - any: + - changed-files: + - any-glob-to-any-file: 'packages/server/src/**/*' + 'area: background': - any: - changed-files: - - any-glob-to-any-file: 'src/background/**/*' + - any-glob-to-any-file: 'packages/extension/src/background/**/*' 'area: ci': - any: @@ -11,12 +16,12 @@ 'area: content': - any: - changed-files: - - any-glob-to-any-file: 'src/content/**/*' + - any-glob-to-any-file: 'packages/extension/src/content/**/*' 'area: popup': - any: - changed-files: - - any-glob-to-any-file: 'src/popup/**/*' + - any-glob-to-any-file: 'packages/extension/src/popup/**/*' 'area: documentation': - any: diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 9e04e362..d2ceae19 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -14,8 +14,27 @@ concurrency: # TODO(@raducristianpopa): add lint/format checks and tests jobs: - build: - name: Build + checks: + name: ESLint, Prettier & Typecheck + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Environment setup + uses: ./.github/actions/setup + + - name: Lint + run: pnpm lint + + - name: Format + run: pnpm format + + - name: Typecheck + run: pnpm typecheck + + build-extension: + name: Build Extension strategy: fail-fast: false matrix: @@ -30,17 +49,17 @@ jobs: - name: Build shell: bash - run: pnpm build ${{ matrix.browser }} + run: pnpm extension build ${{ matrix.browser }} - name: Upload artifacts uses: actions/upload-artifact@v3.1.2 with: name: ${{ github.event.pull_request.number }}-${{ matrix.browser }} - path: dist/${{ matrix.browser }}/${{ matrix.browser }}.zip + path: packages/extension/dist/${{ matrix.browser }}/${{ matrix.browser }}.zip if-no-files-found: error - test: - name: Test + test-extension: + name: Test Extension runs-on: ubuntu-22.04 steps: - name: Checkout repository @@ -50,4 +69,33 @@ jobs: uses: ./.github/actions/setup - name: Test - run: pnpm test:ci + run: pnpm extension test:ci + + build-server: + name: Build Server + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Environment setup + uses: ./.github/actions/setup + + - name: Build + shell: bash + run: pnpm wm-server build + + # TODO: add tests for server + # test-server: + # name: Test Server + # runs-on: ubuntu-22.04 + # steps: + # - name: Checkout repository + # uses: actions/checkout@v3 + + # - name: Environment setup + # uses: ./.github/actions/setup + + # - name: Test + # shell: bash + # run: pnpm wm-server test:ci diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87f6f127..d3ada03a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: - name: Build shell: bash - run: pnpm build + run: pnpm extension build - name: Release uses: softprops/action-gh-release@v1 @@ -49,7 +49,7 @@ jobs: prerelease: false generate_release_notes: true files: | - ./dist/chrome/chrome.zip - ./dist/firefox/firefox.zip - ./dist/opera/opera.zip - ./dist/edge/edge.zip + ./packages/extension/dist/chrome/chrome.zip + ./packages/extension/dist/firefox/firefox.zip + ./packages/extension/dist/opera/opera.zip + ./packages/extension/dist/edge/edge.zip diff --git a/.github/workflows/sanity.yml b/.github/workflows/sanity.yml index 4eaf55d4..894618b0 100644 --- a/.github/workflows/sanity.yml +++ b/.github/workflows/sanity.yml @@ -5,10 +5,29 @@ on: branches: - main -# TODO(@raducristianpopa): add lint/format checks and tests +# TODO(@raducristianpopa): add server tests jobs: - build: + checks: + name: ESLint, Prettier, Typecheck + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Environment setup + uses: ./.github/actions/setup + + - name: Lint + run: pnpm lint + + - name: Format + run: pnpm format + + - name: Typecheck + run: pnpm typecheck + + build-extension: name: Build extension strategy: fail-fast: false @@ -24,10 +43,10 @@ jobs: - name: Build shell: bash - run: pnpm build ${{ matrix.browser}} + run: pnpm extension build ${{ matrix.browser}} - test: - name: Test + test-extension: + name: Test Extension runs-on: ubuntu-22.04 steps: - name: Checkout repository @@ -37,4 +56,32 @@ jobs: uses: ./.github/actions/setup - name: Test - run: pnpm test:ci + run: pnpm extension test:ci + + build-server: + name: Build Server + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Environment setup + uses: ./.github/actions/setup + + - name: Build + shell: bash + run: pnpm wm-server build + + # test-server: + # name: Test Server + # runs-on: ubuntu-22.04 + # steps: + # - name: Checkout repository + # uses: actions/checkout@v3 + + # - name: Environment setup + # uses: ./.github/actions/setup + + # - name: Test + # shell: bash + # run: pnpm wm-server test:ci diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..f8b3d7de --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +auto-install-peers=true +engine-strict=true diff --git a/.prettierignore b/.prettierignore index 26f5c409..3d96fd5e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,8 @@ public generated dist +temp +dev pnpm-lock.yaml *.svg .gitignore diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..ad2fae3b --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,13 @@ +/** @typedef {import("prettier").Config} PrettierConfig */ + +/** @type { PrettierConfig } */ +module.exports = { + singleQuote: true, + printWidth: 100, + trailingComma: 'all', + quoteProps: 'as-needed', + bracketSpacing: true, + arrowParens: 'avoid', + bracketSameLine: false, + semi: true, +}; diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index e5e9ede8..00000000 --- a/.prettierrc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "singleQuote": true, - "printWidth": 100, - "trailingComma": "all", - "quoteProps": "as-needed", - "bracketSpacing": true, - "arrowParens": "avoid", - "bracketSameLine": true, - "semi": false -} diff --git a/README.md b/README.md index 2d9c2ff3..ac5cd605 100755 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@ ![Github Actions CI](https://github.com/interledger/web-monetization-extension/actions/workflows/sanity.yml/badge.svg?branch=main) [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://prettier.io/) -Web Monetization is a browser extension that detects Web Monetization on websites using a browser API that allows the -creation of a payment stream from the user agent to the website. This extension is built with React, TypeScript, and -Vite. +Web Monetization is a browser extension that detects Web Monetization on websites using a browser API that allows the creation of a payment stream from the user agent to the website. ### Contributing @@ -19,26 +17,35 @@ Please read the [contribution guidelines](.github/CONTRIBUTING.md) before submit ### Development -To run the extension in development mode with hot reload, use the following command: +To run the extension and the Web Monetization server in development mode with hot reload, use the following command: -`pnpm dev` +```sh +pnpm dev # default Chrome +pnpm dev firefox # For Firefox +pnpm dev opera # For Opera +pnpm dev edge # For Edge +``` -This command builds the extension using Vite's hot reload feature, allowing you to see immediate changes in the browser -as you modify the code. +### Building the extension -### Building the Extension +To build the extension for production (all browsers), use the following command: -To build the extension for production, use the following command: +```sh +pnpm extension build +``` -`pnpm build` +### Building the extension for a specific browser -### Building the Extension for Firefox +To build the extension for a specific browser, use one of the following commands: -To build the extension for Firefox, use the following command: +```sh +pnpm extension build chrome +pnpm extension build firefox +pnpm extension build opera +pnpm extension build edge +``` -`pnpm build firefox` - -This command transpiles the TypeScript code and generates a production-ready build of the extension in the dist +The `build` command transpiles the TypeScript code and generates a production-ready build of the extension in the `packages/extension/dist` directory. ### Installing the Extension in Chrome @@ -77,7 +84,7 @@ To install the extension in Chrome, follow these steps: 4. Select the Extension File:
Navigate to the folder where you extracted the extension files and select the manifest.json file or the main folder of the extension. -### Testing the Extension +### Testing To run the tests, use the following command: @@ -85,11 +92,9 @@ To run the tests, use the following command: This command runs the tests using Jest and generates a coverage report in the coverage directory. -### Linting and Pre-Commit Hooks +### Linting -The extension is set up with ESLint and Prettier for code linting and formatting. Husky and lint-staged are configured -to enforce linting and formatting rules before each commit. When you commit changes, Husky will run the linting and -formatting tasks to ensure code quality. +The extension is set up with ESLint and Prettier for code linting and formatting. ## License diff --git a/jest.config.ts b/jest.config.ts index ce9b19f5..fc9ff9c2 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,7 +1,6 @@ export default { preset: 'ts-jest', clearMocks: true, - displayName: 'WM Extension', collectCoverageFrom: [ 'src/**/*.test.{js,jsx,ts,tsx}', '!src/**/*.css', @@ -16,13 +15,11 @@ export default { moduleNameMapper: { '@/(.*)': '/src/$1', }, - setupFilesAfterEnv: ['./jest.setup.ts'], testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], - testEnvironment: 'jsdom', testPathIgnorePatterns: ['/node_modules/', '/jest.config.ts'], transform: { '^.+\\.(js|jsx)$': 'babel-jest', '^.+\\.(ts|tsx)?$': 'ts-jest', '\\.(css|less|scss|sass|svg)$': 'jest-transform-stub', }, -} +}; diff --git a/jest.setup.ts b/jest.setup.ts deleted file mode 100644 index 60d87370..00000000 --- a/jest.setup.ts +++ /dev/null @@ -1,5 +0,0 @@ -import '@testing-library/jest-dom' - -import { chrome } from 'jest-chrome' - -Object.assign(global, { chrome: chrome, browser: chrome }) diff --git a/package.json b/package.json index 7cb1f3b6..c38df004 100644 --- a/package.json +++ b/package.json @@ -1,88 +1,42 @@ { - "name": "web-monetization-extension", - "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website.", + "name": "@interledger/web-monetization", "private": true, - "version": "0.2.3", - "license": "Apache-2.0", + "homepage": "https://github.com/interledger/web-monetization-extension", "repository": { "type": "git", - "url": "https://github.com/interledger/web-monetization-extension" + "url": "git@github.com:interledeger/web-monetization-extension.git" }, + "license": "Apache-2.0", + "author": "Interledger Tech Team ", "scripts": { - "analyze": "bash ./scripts/analyze.sh", - "build": "bash ./scripts/build.sh", - "dev": "bash ./scripts/dev.sh", - "lint": "concurrently \"lint:*\"", - "lint:fix": "eslint --ext js,jsx,ts,tsx, src --fix", - "lint:eslint": "eslint . --ext .js,.ts,.tsx --max-warnings 0 --ignore-path .gitignore", - "lint:prettier": "prettier \"**/*.(md|json|yml)\" --ignore-path .gitignore --check", - "lint:type": "tsc --noEmit", - "local-signatures": "pnpm tsx --watch ./local-signatures/index.ts", - "test": "jest --maxWorkers=2 --passWithNoTests", - "test:ci": "pnpm test -- --reporters=default --reporters=github-actions" - }, - "dependencies": { - "axios": "^1.5.1", - "class-variance-authority": "^0.7.0", - "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^11.0.0", - "core-js": "^3.32.1", - "css-loader": "^6.8.1", - "dotenv": "^16.3.1", - "events": "^3.3.0", - "html-webpack-plugin": "^5.5.3", - "prop-types": "^15.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.66.1", - "sass-loader": "^13.3.2", - "style-loader": "^3.3.3", - "tailwind-merge": "^2.1.0", - "terser-webpack-plugin": "^5.3.9", - "uuid": "^9.0.1", - "webextension-polyfill": "^0.10.0", - "webpack": "^5.88.2", - "webpack-bundle-analyzer": "^4.9.1", - "webpack-ext-reloader-mv3": "^2.1.1", - "webpack-extension-manifest-plugin": "^0.8.0", - "zip-webpack-plugin": "^4.0.1" + "build": "pnpm -r build", + "dev": "concurrently -P -n SERVER,EXTESNION -c \"red,blue\" \"pnpm wm-server dev\" \"pnpm extension dev {1}\" --", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "lint": "pnpm -r lint", + "lint:fix": "pnpm -r lint:fix", + "test": "pnpm -r test", + "test:ci": "pnpm -r test:ci", + "typecheck": "pnpm -r typecheck", + "extension": "pnpm --filter @interledger/wm-extension", + "wm-server": "pnpm --filter @interledger/wm-server" }, "devDependencies": { - "@interledger/http-signature-utils": "^2.0.0", - "@tailwindcss/forms": "^0.5.7", "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", - "@types/chrome": "^0.0.244", - "@types/inboxsdk": "^2.0.11", "@types/jest": "^29.5.5", - "@types/jquery": "^3.5.18", - "@types/koa": "^2.13.12", - "@types/koa-bodyparser": "^4.3.12", - "@types/lodash": "^4.14.197", - "@types/node": "^20.8.4", - "@types/react": "^18.2.21", - "@types/react-dom": "^18.2.7", - "@types/react-router-dom": "^5.3.3", - "@types/redux": "^3.6.31", - "@types/uuid": "^9.0.5", - "@types/webextension-polyfill": "^0.10.7", - "@types/webpack-bundle-analyzer": "^4.6.0", - "@types/zip-webpack-plugin": "^3.0.4", + "@types/node": "^18.19.9", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", - "autoprefixer": "^10.4.16", "concurrently": "^8.2.2", "eslint": "^8.48.0", "eslint-config-airbnb": "^19.0.4", - "eslint-config-prettier": "^9.0.0", "eslint-plugin-babel": "^5.3.1", - "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-html": "^7.1.0", "eslint-plugin-import": "^2.28.0", - "eslint-plugin-jest": "^26.1.5", + "eslint-plugin-jest": "^27.6.3", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-simple-import-sort": "^7.0.0", @@ -91,19 +45,13 @@ "jest-chrome": "^0.8.0", "jest-environment-jsdom": "^29.7.0", "jest-transform-stub": "^2.0.0", - "koa": "^2.14.2", - "koa-bodyparser": "^4.4.1", - "postcss": "^8.4.32", - "postcss-loader": "^7.3.3", "prettier": "^3.0.3", - "react-router-dom": "^6.21.1", + "prettier-plugin-tailwindcss": "^0.5.11", "tailwindcss": "^3.4.0", "ts-jest": "^29.1.1", "ts-loader": "^9.4.4", "ts-node": "^10.9.2", - "tsx": "^4.6.2", - "typescript": "^5.2.2", - "webpack-cli": "^5.1.4" + "typescript": "^5.2.2" }, "engines": { "pnpm": "^8.10.5", diff --git a/packages/extension/.eslintrc.js b/packages/extension/.eslintrc.js new file mode 100644 index 00000000..1c0b00a3 --- /dev/null +++ b/packages/extension/.eslintrc.js @@ -0,0 +1,33 @@ +module.exports = { + env: { + es2020: true, + browser: true, + node: true, + }, + globals: { + chrome: true, + browser: true, + }, + extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'], + plugins: ['react', 'react-hooks'], + settings: { + react: { + version: 'detect', + }, + }, + rules: { + 'react/self-closing-comp': [ + 'error', + { + component: true, + html: true, + }, + ], + 'react/prop-types': 'off', + 'react/jsx-props-no-spreading': 'off', + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'error', + 'react/react-in-jsx-scope': 'off', + 'react/jsx-uses-react': 'off', + }, +}; diff --git a/packages/extension/jest.config.ts b/packages/extension/jest.config.ts new file mode 100644 index 00000000..088c80f9 --- /dev/null +++ b/packages/extension/jest.config.ts @@ -0,0 +1,8 @@ +import config from '../../jest.config'; + +export default { + ...config, + displayName: '[WM] Extension', + setupFilesAfterEnv: ['./jest.setup.ts'], + testEnvironment: 'jsdom', +}; diff --git a/packages/extension/jest.setup.ts b/packages/extension/jest.setup.ts new file mode 100644 index 00000000..e524c769 --- /dev/null +++ b/packages/extension/jest.setup.ts @@ -0,0 +1,5 @@ +import '@testing-library/jest-dom'; + +import { chrome } from 'jest-chrome'; + +Object.assign(global, { chrome: chrome, browser: chrome }); diff --git a/packages/extension/package.json b/packages/extension/package.json new file mode 100644 index 00000000..9d2bd0b2 --- /dev/null +++ b/packages/extension/package.json @@ -0,0 +1,77 @@ +{ + "name": "@interledger/wm-extension", + "private": true, + "version": "0.2.3", + "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website.", + "homepage": "https://github.com/interledger/web-monetization-extension/tree/main/packages/extension", + "repository": { + "type": "git", + "url": "git@github.com:interledeger/web-monetization-extension.git", + "directory": "packages/extension" + }, + "license": "Apache-2.0", + "author": "Interledger Tech Team ", + "scripts": { + "analyze": "bash ./scripts/analyze.sh", + "build": "bash ./scripts/build.sh", + "dev": "bash ./scripts/dev.sh", + "format": "prettier --write .", + "lint": "eslint . --ext ts,tsx --cache --cache-location 'node_modules/.cache/.eslintcache' --max-warnings 0", + "lint:fix": "eslint --ext ts,tsx --cache --cache-location 'node_modules/.cache/.eslintcache' --fix", + "typecheck": "tsc --noEmit", + "test": "jest --maxWorkers=2 --passWithNoTests", + "test:ci": "pnpm test -- --reporters=default --reporters=github-actions" + }, + "dependencies": { + "axios": "^1.5.1", + "class-variance-authority": "^0.7.0", + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.8.1", + "events": "^3.3.0", + "html-webpack-plugin": "^5.5.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.21.1", + "sass": "^1.66.1", + "sass-loader": "^13.3.2", + "style-loader": "^3.3.3", + "tailwind-merge": "^2.1.0", + "terser-webpack-plugin": "^5.3.9", + "uuid": "^9.0.1", + "webextension-polyfill": "^0.10.0", + "webpack": "^5.88.2", + "webpack-bundle-analyzer": "^4.9.1", + "webpack-ext-reloader-mv3": "^2.1.1", + "webpack-extension-manifest-plugin": "^0.8.0", + "zip-webpack-plugin": "^4.0.1" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.7", + "@types/chrome": "^0.0.244", + "@types/inboxsdk": "^2.0.11", + "@types/jest": "^29.5.5", + "@types/node": "^20.8.4", + "@types/react": "^18.2.21", + "@types/react-dom": "^18.2.7", + "@types/react-router-dom": "^5.3.3", + "@types/uuid": "^9.0.5", + "@types/webextension-polyfill": "^0.10.7", + "@types/webpack-bundle-analyzer": "^4.6.0", + "@types/zip-webpack-plugin": "^3.0.4", + "@typescript-eslint/eslint-plugin": "^6.5.0", + "@typescript-eslint/parser": "^6.5.0", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.32", + "postcss-loader": "^7.3.3", + "tailwindcss": "^3.4.0", + "webpack-cli": "^5.1.4" + }, + "engines": { + "pnpm": "^8.10.5", + "npm": "pnpm", + "yarn": "pnpm", + "node": "^18.19.0" + }, + "packageManager": "pnpm@8.10.5" +} diff --git a/scripts/analyze.sh b/packages/extension/scripts/analyze.sh similarity index 100% rename from scripts/analyze.sh rename to packages/extension/scripts/analyze.sh diff --git a/scripts/build.sh b/packages/extension/scripts/build.sh similarity index 100% rename from scripts/build.sh rename to packages/extension/scripts/build.sh diff --git a/packages/extension/scripts/dev.sh b/packages/extension/scripts/dev.sh new file mode 100755 index 00000000..9c6c6632 --- /dev/null +++ b/packages/extension/scripts/dev.sh @@ -0,0 +1,9 @@ +#!/bin/bash -u + +set -ex + +TARGET=${1:-chrome} +NODE_ENV=development + +echo Running ${NODE_ENV} build for ${TARGET}... +NODE_ENV=${NODE_ENV} TARGET=${TARGET} pnpm webpack diff --git a/packages/extension/src/_locales/en/messages.json b/packages/extension/src/_locales/en/messages.json new file mode 100755 index 00000000..e3a1b4f3 --- /dev/null +++ b/packages/extension/src/_locales/en/messages.json @@ -0,0 +1,14 @@ +{ + "appName": { + "message": "Web Monetization", + "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website." + }, + "appDescription": { + "message": "Web Monetization Extension", + "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website." + }, + "btnTooltip": { + "message": "Web Monetization", + "description": "..." + } +} diff --git a/src/assets/icons/icon-128.png b/packages/extension/src/assets/icons/icon-128.png similarity index 100% rename from src/assets/icons/icon-128.png rename to packages/extension/src/assets/icons/icon-128.png diff --git a/src/assets/icons/icon-34.png b/packages/extension/src/assets/icons/icon-34.png similarity index 100% rename from src/assets/icons/icon-34.png rename to packages/extension/src/assets/icons/icon-34.png diff --git a/src/assets/icons/icon-active-128.png b/packages/extension/src/assets/icons/icon-active-128.png similarity index 100% rename from src/assets/icons/icon-active-128.png rename to packages/extension/src/assets/icons/icon-active-128.png diff --git a/src/assets/icons/icon-active-34.png b/packages/extension/src/assets/icons/icon-active-34.png similarity index 100% rename from src/assets/icons/icon-active-34.png rename to packages/extension/src/assets/icons/icon-active-34.png diff --git a/src/assets/icons/icon-inactive-128.png b/packages/extension/src/assets/icons/icon-inactive-128.png similarity index 100% rename from src/assets/icons/icon-inactive-128.png rename to packages/extension/src/assets/icons/icon-inactive-128.png diff --git a/src/assets/icons/icon-inactive-34.png b/packages/extension/src/assets/icons/icon-inactive-34.png similarity index 100% rename from src/assets/icons/icon-inactive-34.png rename to packages/extension/src/assets/icons/icon-inactive-34.png diff --git a/src/assets/images/check.svg b/packages/extension/src/assets/images/check.svg similarity index 100% rename from src/assets/images/check.svg rename to packages/extension/src/assets/images/check.svg diff --git a/src/assets/images/close.svg b/packages/extension/src/assets/images/close.svg similarity index 100% rename from src/assets/images/close.svg rename to packages/extension/src/assets/images/close.svg diff --git a/src/assets/images/dollar.svg b/packages/extension/src/assets/images/dollar.svg similarity index 100% rename from src/assets/images/dollar.svg rename to packages/extension/src/assets/images/dollar.svg diff --git a/src/assets/images/edit.svg b/packages/extension/src/assets/images/edit.svg similarity index 100% rename from src/assets/images/edit.svg rename to packages/extension/src/assets/images/edit.svg diff --git a/src/assets/images/logo.svg b/packages/extension/src/assets/images/logo.svg similarity index 100% rename from src/assets/images/logo.svg rename to packages/extension/src/assets/images/logo.svg diff --git a/src/assets/images/web-monetization-fail.svg b/packages/extension/src/assets/images/web-monetization-fail.svg similarity index 100% rename from src/assets/images/web-monetization-fail.svg rename to packages/extension/src/assets/images/web-monetization-fail.svg diff --git a/src/assets/images/web-monetization-success.svg b/packages/extension/src/assets/images/web-monetization-success.svg similarity index 100% rename from src/assets/images/web-monetization-success.svg rename to packages/extension/src/assets/images/web-monetization-success.svg diff --git a/packages/extension/src/background/config.ts b/packages/extension/src/background/config.ts new file mode 100644 index 00000000..5198fb72 --- /dev/null +++ b/packages/extension/src/background/config.ts @@ -0,0 +1,5 @@ +declare const CONFIG_SIGNATURES_URL: string; +declare const CONFIG_WM_WALLET_ADDRESS: string; + +export const SIGNATURES_URL = CONFIG_SIGNATURES_URL; +export const WM_WALLET_ADDRESS = CONFIG_WM_WALLET_ADDRESS; diff --git a/src/background/grant/confirmPayment.ts b/packages/extension/src/background/grant/confirmPayment.ts similarity index 55% rename from src/background/grant/confirmPayment.ts rename to packages/extension/src/background/grant/confirmPayment.ts index b7374727..5d538082 100644 --- a/src/background/grant/confirmPayment.ts +++ b/packages/extension/src/background/grant/confirmPayment.ts @@ -1,12 +1,12 @@ -import { tabs } from 'webextension-polyfill' +import { tabs } from 'webextension-polyfill'; const getCurrentActiveTabId = async () => { - const activeTabs = await tabs.query({ active: true, currentWindow: true }) - return activeTabs[0].id -} + const activeTabs = await tabs.query({ active: true, currentWindow: true }); + return activeTabs[0].id; +}; export const confirmPayment = async (url: string) => { - const currentTabId = await getCurrentActiveTabId() + const currentTabId = await getCurrentActiveTabId(); return await new Promise(resolve => { if (url) { @@ -14,20 +14,20 @@ export const confirmPayment = async (url: string) => { if (tab.id) { tabs.onUpdated.addListener((tabId, changeInfo) => { try { - const tabUrl = new URL(changeInfo.url || '') - const interactRef = tabUrl.searchParams.get('interact_ref') + const tabUrl = new URL(changeInfo.url || ''); + const interactRef = tabUrl.searchParams.get('interact_ref'); if (tabId === tab.id && interactRef) { - tabs.update(currentTabId, { active: true }) - tabs.remove(tab.id) - resolve(interactRef) + tabs.update(currentTabId, { active: true }); + tabs.remove(tab.id); + resolve(interactRef); } } catch (e) { - throw new Error('Invalid interact ref url.') + throw new Error('Invalid interact ref url.'); } - }) + }); } - }) + }); } - }) -} + }); +}; diff --git a/src/background/grant/createQuote.ts b/packages/extension/src/background/grant/createQuote.ts similarity index 64% rename from src/background/grant/createQuote.ts rename to packages/extension/src/background/grant/createQuote.ts index 5262bdd4..731bd46a 100644 --- a/src/background/grant/createQuote.ts +++ b/packages/extension/src/background/grant/createQuote.ts @@ -1,15 +1,15 @@ -import { AxiosInstance } from 'axios' +import { AxiosInstance } from 'axios'; -import { getHeaders } from './getHeaders' +import { getHeaders } from './getHeaders'; type TCreateQuote = (_params: { - receiver: string - walletAddress: any - sendingUrl: string - token: string - amount: string - instance: AxiosInstance -}) => Promise + receiver: string; + walletAddress: any; + sendingUrl: string; + token: string; + amount: string; + instance: AxiosInstance; +}) => Promise; export const createQuote: TCreateQuote = async ({ receiver, @@ -28,17 +28,17 @@ export const createQuote: TCreateQuote = async ({ assetCode: walletAddress.assetCode, // 'USD' assetScale: walletAddress.assetScale, // 9 }, - } + }; const quote = await instance.post( new URL(sendingUrl).origin + '/quotes', payload, getHeaders(token), - ) + ); if (!quote.data.id) { - throw new Error('No quote url id') + throw new Error('No quote url id'); } - return quote.data.id -} + return quote.data.id; +}; diff --git a/src/background/grant/getContinuationRequest.ts b/packages/extension/src/background/grant/getContinuationRequest.ts similarity index 67% rename from src/background/grant/getContinuationRequest.ts rename to packages/extension/src/background/grant/getContinuationRequest.ts index 6947f267..f3c23cdd 100644 --- a/src/background/grant/getContinuationRequest.ts +++ b/packages/extension/src/background/grant/getContinuationRequest.ts @@ -1,13 +1,13 @@ -import { AxiosInstance } from 'axios' +import { AxiosInstance } from 'axios'; -import { getHeaders } from './getHeaders' +import { getHeaders } from './getHeaders'; type TGetContinuationRequest = (_params: { - url: string - interactRef: any - token: string - instance: AxiosInstance -}) => Promise + url: string; + interactRef: any; + token: string; + instance: AxiosInstance; +}) => Promise; export const getContinuationRequest: TGetContinuationRequest = async ({ url, @@ -21,14 +21,14 @@ export const getContinuationRequest: TGetContinuationRequest = async ({ interact_ref: interactRef, }, getHeaders(token), - ) + ); if (!continuationRequest.data.access_token.value) { - throw new Error('No continuation request') + throw new Error('No continuation request'); } return { manageUrl: continuationRequest.data.access_token.manage, continuationRequestToken: continuationRequest.data.access_token.value, - } -} + }; +}; diff --git a/src/background/grant/getHeaders.ts b/packages/extension/src/background/grant/getHeaders.ts similarity index 96% rename from src/background/grant/getHeaders.ts rename to packages/extension/src/background/grant/getHeaders.ts index 803f7b03..0b71ec99 100644 --- a/src/background/grant/getHeaders.ts +++ b/packages/extension/src/background/grant/getHeaders.ts @@ -2,4 +2,4 @@ export const getHeaders = (gnapToken: string) => ({ headers: { Authorization: `GNAP ${gnapToken}`, }, -}) +}); diff --git a/src/background/grant/getIcomingPaymentGrant.ts b/packages/extension/src/background/grant/getIcomingPaymentGrant.ts similarity index 68% rename from src/background/grant/getIcomingPaymentGrant.ts rename to packages/extension/src/background/grant/getIcomingPaymentGrant.ts index df0e2f09..2dd22e73 100644 --- a/src/background/grant/getIcomingPaymentGrant.ts +++ b/packages/extension/src/background/grant/getIcomingPaymentGrant.ts @@ -1,11 +1,11 @@ -import { AxiosInstance } from 'axios' +import { AxiosInstance } from 'axios'; type TGetIncomingPaymentGrant = (_params: { - client: string - identifier: string - wallet: Record - instance: AxiosInstance -}) => Promise + client: string; + identifier: string; + wallet: Record; + instance: AxiosInstance; +}) => Promise; export const getIncomingPaymentGrant: TGetIncomingPaymentGrant = async ({ client, @@ -24,13 +24,13 @@ export const getIncomingPaymentGrant: TGetIncomingPaymentGrant = async ({ ], }, client, // WM_PAYMENT_POINTER_URL - } + }; - const response = await instance.post(wallet.authServer + '/', payload) + const response = await instance.post(wallet.authServer + '/', payload); if (!response.data.access_token.value) { - throw new Error('No client auth') + throw new Error('No client auth'); } - return response.data.access_token.value -} + return response.data.access_token.value; +}; diff --git a/src/background/grant/getIncomingPaymentUrlId.ts b/packages/extension/src/background/grant/getIncomingPaymentUrlId.ts similarity index 64% rename from src/background/grant/getIncomingPaymentUrlId.ts rename to packages/extension/src/background/grant/getIncomingPaymentUrlId.ts index 18fbbe9c..a78627db 100644 --- a/src/background/grant/getIncomingPaymentUrlId.ts +++ b/packages/extension/src/background/grant/getIncomingPaymentUrlId.ts @@ -1,12 +1,12 @@ -import { AxiosInstance } from 'axios' +import { AxiosInstance } from 'axios'; -import { getHeaders } from './getHeaders' +import { getHeaders } from './getHeaders'; type TGetIncomingPaymentUrlId = (_params: { - walletAddress: string - token: string - instance: AxiosInstance -}) => Promise + walletAddress: string; + token: string; + instance: AxiosInstance; +}) => Promise; export const getIncomingPaymentUrlId: TGetIncomingPaymentUrlId = async ({ walletAddress, @@ -20,11 +20,11 @@ export const getIncomingPaymentUrlId: TGetIncomingPaymentUrlId = async ({ expiresAt: new Date(Date.now() + 6000 * 60 * 10).toISOString(), }, getHeaders(token), - ) + ); if (!incomingPayment?.data?.id) { - throw new Error('No incoming payment id') + throw new Error('No incoming payment id'); } - return incomingPayment.data.id -} + return incomingPayment.data.id; +}; diff --git a/src/background/grant/getOutgoingPaymentGrant.ts b/packages/extension/src/background/grant/getOutgoingPaymentGrant.ts similarity index 83% rename from src/background/grant/getOutgoingPaymentGrant.ts rename to packages/extension/src/background/grant/getOutgoingPaymentGrant.ts index a9b258c2..7e68f137 100644 --- a/src/background/grant/getOutgoingPaymentGrant.ts +++ b/packages/extension/src/background/grant/getOutgoingPaymentGrant.ts @@ -1,12 +1,12 @@ -import { AxiosInstance } from 'axios' +import { AxiosInstance } from 'axios'; type TGetOutgoingPaymentGrant = (_params: { - client: string - identifier: string - wallet: Record - amount: string | number - instance: AxiosInstance -}) => Promise + client: string; + identifier: string; + wallet: Record; + amount: string | number; + instance: AxiosInstance; +}) => Promise; export const getOutgoingPaymentGrant: TGetOutgoingPaymentGrant = async ({ client, @@ -44,16 +44,16 @@ export const getOutgoingPaymentGrant: TGetOutgoingPaymentGrant = async ({ nonce: new Date().getTime().toString(), }, }, - } + }; - const outgoingPaymentGrant = await instance.post(wallet.authServer + '/', payload) + const outgoingPaymentGrant = await instance.post(wallet.authServer + '/', payload); if (!outgoingPaymentGrant.data.interact.redirect) { - throw new Error('No redirect') + throw new Error('No redirect'); } return { outgoingPaymentGrantToken: outgoingPaymentGrant.data.continue.access_token.value, outgoingPaymentGrantData: outgoingPaymentGrant.data, - } -} + }; +}; diff --git a/src/background/grant/getQuoteGrant.ts b/packages/extension/src/background/grant/getQuoteGrant.ts similarity index 63% rename from src/background/grant/getQuoteGrant.ts rename to packages/extension/src/background/grant/getQuoteGrant.ts index 1c449e35..aa62b7f0 100644 --- a/src/background/grant/getQuoteGrant.ts +++ b/packages/extension/src/background/grant/getQuoteGrant.ts @@ -1,11 +1,11 @@ -import { AxiosInstance } from 'axios' +import { AxiosInstance } from 'axios'; type TGetQuoteGrant = (_params: { - client: string - identifier: string - wallet: Record - instance: AxiosInstance -}) => Promise + client: string; + identifier: string; + wallet: Record; + instance: AxiosInstance; +}) => Promise; export const getQuoteGrant: TGetQuoteGrant = async ({ client, identifier, wallet, instance }) => { const quotePayload = { @@ -19,12 +19,12 @@ export const getQuoteGrant: TGetQuoteGrant = async ({ client, identifier, wallet ], }, client, // WM_PAYMENT_POINTER_URL - } - const quoteGrant = await instance.post(wallet.authServer + '/', quotePayload) + }; + const quoteGrant = await instance.post(wallet.authServer + '/', quotePayload); if (!quoteGrant.data?.access_token?.value) { - throw new Error('No quote grant') + throw new Error('No quote grant'); } - return quoteGrant.data.access_token.value -} + return quoteGrant.data.access_token.value; +}; diff --git a/src/background/grant/index.ts b/packages/extension/src/background/grant/index.ts similarity index 64% rename from src/background/grant/index.ts rename to packages/extension/src/background/grant/index.ts index 7364d9b5..0947b7a5 100644 --- a/src/background/grant/index.ts +++ b/packages/extension/src/background/grant/index.ts @@ -1,12 +1,12 @@ -import { confirmPayment } from './confirmPayment' -import { createQuote } from './createQuote' -import { getContinuationRequest } from './getContinuationRequest' -import { getHeaders } from './getHeaders' -import { getIncomingPaymentGrant } from './getIcomingPaymentGrant' -import { getIncomingPaymentUrlId } from './getIncomingPaymentUrlId' -import { getOutgoingPaymentGrant } from './getOutgoingPaymentGrant' -import { getQuoteGrant } from './getQuoteGrant' -import { rotateToken } from './rotateToken' +import { confirmPayment } from './confirmPayment'; +import { createQuote } from './createQuote'; +import { getContinuationRequest } from './getContinuationRequest'; +import { getHeaders } from './getHeaders'; +import { getIncomingPaymentGrant } from './getIcomingPaymentGrant'; +import { getIncomingPaymentUrlId } from './getIncomingPaymentUrlId'; +import { getOutgoingPaymentGrant } from './getOutgoingPaymentGrant'; +import { getQuoteGrant } from './getQuoteGrant'; +import { rotateToken } from './rotateToken'; export { confirmPayment, @@ -18,4 +18,4 @@ export { getOutgoingPaymentGrant, getQuoteGrant, rotateToken, -} +}; diff --git a/src/background/grant/rotateToken.ts b/packages/extension/src/background/grant/rotateToken.ts similarity index 64% rename from src/background/grant/rotateToken.ts rename to packages/extension/src/background/grant/rotateToken.ts index b4d8087f..77927501 100644 --- a/src/background/grant/rotateToken.ts +++ b/packages/extension/src/background/grant/rotateToken.ts @@ -1,26 +1,26 @@ -import { AxiosInstance } from 'axios' +import { AxiosInstance } from 'axios'; -import { getHeaders } from '@/background/grant/getHeaders' +import { getHeaders } from '@/background/grant/getHeaders'; type TRotateToken = (_params: { - url: string - token: string - instance: AxiosInstance -}) => Promise + url: string; + token: string; + instance: AxiosInstance; +}) => Promise; export const rotateToken: TRotateToken = async ({ url, token, instance }) => { const response = await instance.post( url, // this.manageUrl undefined, getHeaders(token), // this.continuationRequestToken - ) + ); if (!response.data.access_token.value) { - throw new Error('No continuation request') + throw new Error('No continuation request'); } return { manageUrl: response.data.access_token.manage, continuationRequestToken: response.data.access_token.value, - } -} + }; +}; diff --git a/src/background/grantFlow.ts b/packages/extension/src/background/grantFlow.ts similarity index 71% rename from src/background/grantFlow.ts rename to packages/extension/src/background/grantFlow.ts index 18fac5fb..0ad90f39 100644 --- a/src/background/grantFlow.ts +++ b/packages/extension/src/background/grantFlow.ts @@ -1,6 +1,6 @@ -import { tabs } from 'webextension-polyfill' +import { tabs } from 'webextension-polyfill'; -import { WM_WALLET_ADDRESS } from '@/background/config' +import { WM_WALLET_ADDRESS } from '@/background/config'; import { createQuote, getHeaders, @@ -8,67 +8,67 @@ import { getIncomingPaymentUrlId, getOutgoingPaymentGrant, getQuoteGrant, -} from '@/background/grant' -import { confirmPayment } from '@/background/grant/confirmPayment' -import { getContinuationRequest } from '@/background/grant/getContinuationRequest' -import { getAxiosInstance } from '@/background/requestConfig' +} from '@/background/grant'; +import { confirmPayment } from '@/background/grant/confirmPayment'; +import { getContinuationRequest } from '@/background/grant/getContinuationRequest'; +import { getAxiosInstance } from '@/background/requestConfig'; export class PaymentFlowService { - axiosInstance = getAxiosInstance() + axiosInstance = getAxiosInstance(); - sendingPaymentPointerUrl: string - receivingPaymentPointerUrl: string + sendingPaymentPointerUrl: string; + receivingPaymentPointerUrl: string; - incomingPaymentUrlId: string - quoteUrlId: string - outgoingPaymentGrantData: any + incomingPaymentUrlId: string; + quoteUrlId: string; + outgoingPaymentGrantData: any; - clientAuthToken: string - quoteGrantToken: string - outgoingPaymentGrantToken: string - continuationRequestToken: string - interactRef: string + clientAuthToken: string; + quoteGrantToken: string; + outgoingPaymentGrantToken: string; + continuationRequestToken: string; + interactRef: string; - manageUrl: string + manageUrl: string; - amount: string | number + amount: string | number; - sendingWalletAddress: any - receivingWalletAddress: any + sendingWalletAddress: any; + receivingWalletAddress: any; constructor( sendingPaymentPointerUrl: string, receivingPaymentPointerUrl: string, amount: string, ) { - this.sendingPaymentPointerUrl = sendingPaymentPointerUrl - this.receivingPaymentPointerUrl = receivingPaymentPointerUrl - this.amount = amount + this.sendingPaymentPointerUrl = sendingPaymentPointerUrl; + this.receivingPaymentPointerUrl = receivingPaymentPointerUrl; + this.amount = amount; } async initPaymentFlow() { - this.sendingWalletAddress = await this.getWalletAddress(this.sendingPaymentPointerUrl) - this.receivingWalletAddress = await this.getWalletAddress(this.receivingPaymentPointerUrl) + this.sendingWalletAddress = await this.getWalletAddress(this.sendingPaymentPointerUrl); + this.receivingWalletAddress = await this.getWalletAddress(this.receivingPaymentPointerUrl); this.clientAuthToken = await getIncomingPaymentGrant({ client: WM_WALLET_ADDRESS, identifier: this.receivingPaymentPointerUrl, wallet: this.receivingWalletAddress, instance: this.axiosInstance, - }) + }); this.incomingPaymentUrlId = await getIncomingPaymentUrlId({ walletAddress: this.receivingPaymentPointerUrl, token: this.clientAuthToken, instance: this.axiosInstance, - }) + }); this.quoteGrantToken = await getQuoteGrant({ client: WM_WALLET_ADDRESS, identifier: this.sendingPaymentPointerUrl, wallet: this.sendingWalletAddress, instance: this.axiosInstance, - }) + }); const outgoingData = await getOutgoingPaymentGrant({ client: WM_WALLET_ADDRESS, @@ -76,25 +76,25 @@ export class PaymentFlowService { wallet: this.sendingWalletAddress, amount: this.amount, instance: this.axiosInstance, - }) + }); - this.outgoingPaymentGrantToken = outgoingData.outgoingPaymentGrantToken - this.outgoingPaymentGrantData = outgoingData.outgoingPaymentGrantData + this.outgoingPaymentGrantToken = outgoingData.outgoingPaymentGrantToken; + this.outgoingPaymentGrantData = outgoingData.outgoingPaymentGrantData; - this.interactRef = await confirmPayment(this.outgoingPaymentGrantData.interact.redirect) + this.interactRef = await confirmPayment(this.outgoingPaymentGrantData.interact.redirect); const continuationRequest = await getContinuationRequest({ url: this.outgoingPaymentGrantData.continue.uri, interactRef: this.interactRef, token: this.outgoingPaymentGrantToken, instance: this.axiosInstance, - }) + }); - this.manageUrl = continuationRequest.manageUrl - this.continuationRequestToken = continuationRequest.continuationRequestToken + this.manageUrl = continuationRequest.manageUrl; + this.continuationRequestToken = continuationRequest.continuationRequestToken; - const currentTabId = await this.getCurrentActiveTabId() - await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' }) + const currentTabId = await this.getCurrentActiveTabId(); + await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' }); } async getWalletAddress(paymentPointerUrl: string) { @@ -102,37 +102,37 @@ export class PaymentFlowService { headers: { Accept: 'application/json', }, - }) + }); if (!response?.data?.id) { - throw new Error('No client auth') + throw new Error('No client auth'); } - return response.data + return response.data; } async runPayment() { const payload = { walletAddress: this.sendingPaymentPointerUrl, quoteId: this.quoteUrlId, - } + }; const response = await this.axiosInstance.post( new URL(this.sendingPaymentPointerUrl).origin + '/outgoing-payments', payload, getHeaders(this.continuationRequestToken), - ) + ); const { receiveAmount, receiver: incomingPayment, walletAddress: paymentPointer, - } = response.data - const currentTabId = await this.getCurrentActiveTabId() + } = response.data; + const currentTabId = await this.getCurrentActiveTabId(); await tabs.sendMessage(currentTabId ?? 0, { type: 'PAYMENT_SUCCESS', data: { receiveAmount, incomingPayment, paymentPointer }, - }) + }); // console.log('outgoingPayment', outgoingPayment) } @@ -144,13 +144,13 @@ export class PaymentFlowService { token: this.quoteGrantToken, amount: '1000000', instance: this.axiosInstance, - }) + }); - await this.runPayment() + await this.runPayment(); } async getCurrentActiveTabId() { - const activeTabs = await tabs.query({ active: true, currentWindow: true }) - return activeTabs[0].id + const activeTabs = await tabs.query({ active: true, currentWindow: true }); + return activeTabs[0].id; } } diff --git a/src/background/index.ts b/packages/extension/src/background/index.ts similarity index 67% rename from src/background/index.ts rename to packages/extension/src/background/index.ts index 7c65b53a..ebf9eac1 100755 --- a/src/background/index.ts +++ b/packages/extension/src/background/index.ts @@ -1,11 +1,11 @@ -import { action, Runtime, runtime, Tabs, tabs } from 'webextension-polyfill' +import { action, Runtime, runtime, Tabs, tabs } from 'webextension-polyfill'; -import { PaymentFlowService } from '@/background/grantFlow' +import { PaymentFlowService } from '@/background/grantFlow'; -const iconActive34 = runtime.getURL('assets/icons/icon-active-34.png') -const iconActive128 = runtime.getURL('assets/icons/icon-active-128.png') -const iconInactive34 = runtime.getURL('assets/icons/icon-inactive-34.png') -const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png') +const iconActive34 = runtime.getURL('assets/icons/icon-active-34.png'); +const iconActive128 = runtime.getURL('assets/icons/icon-active-128.png'); +const iconInactive34 = runtime.getURL('assets/icons/icon-inactive-34.png'); +const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png'); // const SENDING_PAYMENT_POINTER_URL = 'https://ilp.rafiki.money/wmuser' // cel din extensie al userului // const RECEIVING_PAYMENT_POINTER_URL = 'https://ilp.rafiki.money/web-page' // cel din dom @@ -15,13 +15,13 @@ const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png') * @type {class} */ class Background { - _port: number - grantFlow: PaymentFlowService | null = null - spentAmount: number = 0 - paymentStarted = false + _port: number; + grantFlow: PaymentFlowService | null = null; + spentAmount: number = 0; + paymentStarted = false; constructor() { - this.init() + this.init(); } /** @@ -30,23 +30,23 @@ class Background { * @returns {void} */ init = async () => { - console.log('[===== Loaded Background Scripts =====]') + console.log('[===== Loaded Background Scripts =====]'); //When extension installed - runtime.onInstalled.addListener(this.onInstalled) + runtime.onInstalled.addListener(this.onInstalled); //Add message listener in Browser. - runtime.onMessage.addListener(this.onMessage) + runtime.onMessage.addListener(this.onMessage); //Add Update listener for tab - tabs.onUpdated.addListener(this.onUpdatedTab) + tabs.onUpdated.addListener(this.onUpdatedTab); //Add New tab create listener - tabs.onCreated.addListener(this.onCreatedTab) + tabs.onCreated.addListener(this.onCreatedTab); //Add tab change listener - tabs.onActivated.addListener(this.handleTabChange) - } + tabs.onActivated.addListener(this.handleTabChange); + }; //TODO: Listeners @@ -54,8 +54,8 @@ class Background { * Extension Installed */ onInstalled = () => { - console.log('[===== Installed Extension!] =====') - } + console.log('[===== Installed Extension!] ====='); + }; /** * Message Handler Function @@ -66,13 +66,13 @@ class Background { */ onMessage = async (message: EXTMessage, sender: Runtime.MessageSender) => { try { - console.log('[===== Received message =====]', message, sender) + console.log('[===== Received message =====]', message, sender); switch (message.type) { case 'IS_MONETIZATION_READY': { if (message?.data) { - this.updateIcon(message.data.monetization) + this.updateIcon(message.data.monetization); } - break + break; } case 'SET_INCOMING_POINTER': { @@ -80,66 +80,66 @@ class Background { incomingPayment: receivingPaymentPointerUrl, paymentPointer: sendingPaymentPointerUrl, amount, - } = message.data + } = message.data; if (this.grantFlow?.sendingPaymentPointerUrl === sendingPaymentPointerUrl) { if (!this.paymentStarted) { - this.paymentStarted = true - const currentTabId = await this.grantFlow?.getCurrentActiveTabId() - await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' }) + this.paymentStarted = true; + const currentTabId = await this.grantFlow?.getCurrentActiveTabId(); + await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' }); } } else { this.grantFlow = new PaymentFlowService( sendingPaymentPointerUrl, receivingPaymentPointerUrl, amount, - ) + ); - this.grantFlow.initPaymentFlow() + this.grantFlow.initPaymentFlow(); } - break + break; } case 'GET_SENDING_PAYMENT_POINTER': { if (this.grantFlow) { - const { sendingPaymentPointerUrl, amount } = this.grantFlow + const { sendingPaymentPointerUrl, amount } = this.grantFlow; return { type: 'SUCCESS', data: { sendingPaymentPointerUrl, amount, started: this.paymentStarted }, - } + }; } return { type: 'ERROR', data: { sendingPaymentPointerUrl: '' }, - } + }; } case 'RUN_PAYMENT': { if (this.grantFlow) { - this.grantFlow.sendPayment() + this.grantFlow.sendPayment(); this.spentAmount = Number( parseFloat(String(this.spentAmount + 1000000 / 10 ** 9)).toFixed(3), - ) - this.sendSpendAmount() - this.paymentStarted = true + ); + this.sendSpendAmount(); + this.paymentStarted = true; } - break + break; } case 'PAUSE_PAYMENTS': { - this.paymentStarted = false - break + this.paymentStarted = false; + break; } } - return true // result to reply + return true; // result to reply } catch (error) { - console.log('[===== Error in MessageListener =====]', error) - return error + console.log('[===== Error in MessageListener =====]', error); + return error; } - } + }; /** * Message from Long Live Connection @@ -147,22 +147,22 @@ class Background { * @param msg */ onMessageFromExtension = (msg: EXTMessage) => { - console.log('[===== Message from Long Live Connection =====]', msg) - } + console.log('[===== Message from Long Live Connection =====]', msg); + }; /** * * @param tab */ onCreatedTab = (tab: Tabs.Tab) => { - console.log('[===== New Tab Created =====]', tab) - } + console.log('[===== New Tab Created =====]', tab); + }; sendSpendAmount() { runtime.sendMessage({ type: 'SPENT_AMOUNT', data: { spentAmount: this.spentAmount }, - }) + }); } /** @@ -174,12 +174,12 @@ class Background { */ onUpdatedTab = async (tabId: number, changeInfo: Tabs.OnUpdatedChangeInfoType, tab: Tabs.Tab) => { if (tab.status === 'complete' && tab.url?.match(/^http/)) { - const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' }) + const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' }); if (response.data) { - await this.updateIcon(response.data.monetization) + await this.updateIcon(response.data.monetization); } } - } + }; /** * Get url from tabId @@ -187,13 +187,13 @@ class Background { */ getURLFromTab = async (tabId: number) => { try { - const tab = await tabs.get(tabId) - return tab.url || '' + const tab = await tabs.get(tabId); + return tab.url || ''; } catch (error) { - console.log(`[===== Could not get Tab Info$(tabId) in getURLFromTab =====]`, error) - throw '' + console.log(`[===== Could not get Tab Info$(tabId) in getURLFromTab =====]`, error); + throw ''; } - } + }; /** * Open new tab by url @@ -201,13 +201,13 @@ class Background { */ openNewTab = async (url: string) => { try { - const tab = await tabs.create({ url }) - return tab + const tab = await tabs.create({ url }); + return tab; } catch (error) { - console.log(`[===== Error in openNewTab =====]`, error) - return null + console.log(`[===== Error in openNewTab =====]`, error); + return null; } - } + }; /** * Close specific tab @@ -216,49 +216,49 @@ class Background { */ closeTab = async (tab: Tabs.Tab) => { try { - await tabs.remove(tab.id ?? 0) + await tabs.remove(tab.id ?? 0); } catch (error) { - console.log(`[===== Error in closeTab =====]`, error) + console.log(`[===== Error in closeTab =====]`, error); } - } + }; /** * send message */ sendMessage = async (tab: Tabs.Tab, msg: EXTMessage) => { try { - const res = await tabs.sendMessage(tab.id ?? 0, msg) - return res + const res = await tabs.sendMessage(tab.id ?? 0, msg); + return res; } catch (error) { - console.log(`[===== Error in sendMessage =====]`, error) - return null + console.log(`[===== Error in sendMessage =====]`, error); + return null; } - } + }; updateIcon = async (active: boolean) => { const iconData = { '34': active ? iconActive34 : iconInactive34, '128': active ? iconActive128 : iconInactive128, - } + }; if (action) { - await action.setIcon({ path: iconData }) + await action.setIcon({ path: iconData }); } else if (chrome.browserAction) { - chrome.browserAction.setIcon({ path: iconData }) + chrome.browserAction.setIcon({ path: iconData }); } - } + }; handleTabChange = async (activeInfo: chrome.tabs.TabActiveInfo) => { - const tabId = activeInfo.tabId + const tabId = activeInfo.tabId; - const tab = await tabs.get(tabId) + const tab = await tabs.get(tabId); if (tab && tab.url?.includes('https') && tab.status === 'complete') { - const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' }) + const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' }); if (response?.data) { - this.updateIcon(response.data.monetization) + this.updateIcon(response.data.monetization); } } - } + }; } -export const background = new Background() +export const background = new Background(); diff --git a/src/background/requestConfig.ts b/packages/extension/src/background/requestConfig.ts similarity index 85% rename from src/background/requestConfig.ts rename to packages/extension/src/background/requestConfig.ts index 44953061..3dd95a13 100644 --- a/src/background/requestConfig.ts +++ b/packages/extension/src/background/requestConfig.ts @@ -1,6 +1,6 @@ -import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios' +import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios'; -import { SIGNATURES_URL } from '@/background/config' +import { SIGNATURES_URL } from '@/background/config'; export const getAxiosInstance = (timeout = 10000): AxiosInstance => { const axiosInstance = axios.create({ @@ -11,12 +11,12 @@ export const getAxiosInstance = (timeout = 10000): AxiosInstance => { }, }, timeout, - }) + }); axiosInstance.interceptors.request.use( async (config: InternalAxiosRequestConfig) => { if (!config.method || !config.url) { - throw new Error('Cannot intercept request: url or method missing') + throw new Error('Cannot intercept request: url or method missing'); } const payload = { @@ -27,17 +27,17 @@ export const getAxiosInstance = (timeout = 10000): AxiosInstance => { method: config.method.toUpperCase(), url: config.url, body: JSON.stringify(config.data), - } + }; const contentAndSigHeaders = await axios.post(SIGNATURES_URL, payload, { headers: { 'Content-Type': 'application/json' }, - }) + }); - config.headers['Signature'] = contentAndSigHeaders.data['Signature'] - config.headers['Signature-Input'] = contentAndSigHeaders.data['Signature-Input'] - config.headers['Content-Digest'] = contentAndSigHeaders.data['Content-Digest'] + config.headers['Signature'] = contentAndSigHeaders.data['Signature']; + config.headers['Signature-Input'] = contentAndSigHeaders.data['Signature-Input']; + config.headers['Content-Digest'] = contentAndSigHeaders.data['Content-Digest']; - return config + return config; }, undefined, { @@ -45,7 +45,7 @@ export const getAxiosInstance = (timeout = 10000): AxiosInstance => { config.method?.toLowerCase() === 'post' || !!(config.headers && config.headers['Authorization']), }, - ) + ); - return axiosInstance -} + return axiosInstance; +}; diff --git a/src/components/__tests__/button.test.tsx b/packages/extension/src/components/__tests__/button.test.tsx similarity index 58% rename from src/components/__tests__/button.test.tsx rename to packages/extension/src/components/__tests__/button.test.tsx index a9a3b286..a1809236 100644 --- a/src/components/__tests__/button.test.tsx +++ b/packages/extension/src/components/__tests__/button.test.tsx @@ -1,60 +1,60 @@ -import { render } from '@testing-library/react' -import React from 'react' +import { render } from '@testing-library/react'; +import React from 'react'; -import { Button } from '@/components/button' +import { Button } from '@/components/button'; describe('Button', () => { it('should render a button with the `aria-label` attribute', () => { - const { queryByRole } = render() + const { queryByRole } = render(); - expect(queryByRole('button')).toBeInTheDocument() - expect(queryByRole('button')).toHaveAttribute('aria-label', 'test button') - }) + expect(queryByRole('button')).toBeInTheDocument(); + expect(queryByRole('button')).toHaveAttribute('aria-label', 'test button'); + }); it('should default to `type="button"`', () => { - const { queryByRole } = render() + const { queryByRole } = render(); - expect(queryByRole('button')).toBeInTheDocument() - expect(queryByRole('button')).toHaveAttribute('type', 'button') - }) + expect(queryByRole('button')).toBeInTheDocument(); + expect(queryByRole('button')).toHaveAttribute('type', 'button'); + }); it('should not have the `disabled` attribute and `aria-disabled="false"` if `loading` is false', () => { - const { queryByRole } = render() + const { queryByRole } = render(); - expect(queryByRole('button')).toBeInTheDocument() - expect(queryByRole('button')).not.toHaveAttribute('disabled') - expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'false') - expect(queryByRole('button')).not.toBeDisabled() - }) + expect(queryByRole('button')).toBeInTheDocument(); + expect(queryByRole('button')).not.toHaveAttribute('disabled'); + expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'false'); + expect(queryByRole('button')).not.toBeDisabled(); + }); it('should have the `disabled` and `aria-disabled="true"` attributes if `loading` is true', () => { const { queryByRole } = render( , - ) + ); - expect(queryByRole('button')).toBeInTheDocument() - expect(queryByRole('button')).toHaveAttribute('disabled') - expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'true') - expect(queryByRole('button')).toBeDisabled() - }) + expect(queryByRole('button')).toBeInTheDocument(); + expect(queryByRole('button')).toHaveAttribute('disabled'); + expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'true'); + expect(queryByRole('button')).toBeDisabled(); + }); it('should have the `bg-button-base` class by default', () => { - const { queryByRole } = render() + const { queryByRole } = render(); - expect(queryByRole('button')).toBeInTheDocument() - expect(queryByRole('button')).toHaveClass('bg-button-base') - }) + expect(queryByRole('button')).toBeInTheDocument(); + expect(queryByRole('button')).toHaveClass('bg-button-base'); + }); it('should have the `bg-error` class when the `destructive` variant is passed', () => { const { queryByRole } = render( , - ) + ); - expect(queryByRole('button')).toBeInTheDocument() - expect(queryByRole('button')).toHaveClass('bg-error') - }) -}) + expect(queryByRole('button')).toBeInTheDocument(); + expect(queryByRole('button')).toHaveClass('bg-error'); + }); +}); diff --git a/src/components/__tests__/input.test.tsx b/packages/extension/src/components/__tests__/input.test.tsx similarity index 62% rename from src/components/__tests__/input.test.tsx rename to packages/extension/src/components/__tests__/input.test.tsx index 6f66e7a4..7541daef 100644 --- a/src/components/__tests__/input.test.tsx +++ b/packages/extension/src/components/__tests__/input.test.tsx @@ -1,55 +1,55 @@ -import { render } from '@testing-library/react' -import React from 'react' +import { render } from '@testing-library/react'; +import React from 'react'; -import { Input } from '@/components/input' +import { Input } from '@/components/input'; describe('Input', () => { it('should default to `type="text"`', () => { - const { queryByLabelText } = render() + const { queryByLabelText } = render(); - expect(queryByLabelText('test input')).toBeInTheDocument() - expect(queryByLabelText('test input')).toHaveAttribute('type', 'text') - }) + expect(queryByLabelText('test input')).toBeInTheDocument(); + expect(queryByLabelText('test input')).toHaveAttribute('type', 'text'); + }); it('should not have the `disabled` attribute and `aria-disabled="false"` if `loading` is false', () => { - const { queryByLabelText } = render() + const { queryByLabelText } = render(); - expect(queryByLabelText('test input')).toBeInTheDocument() - expect(queryByLabelText('test input')).not.toHaveAttribute('disabled') - expect(queryByLabelText('test input')).toHaveAttribute('aria-disabled', 'false') - expect(queryByLabelText('test input')).not.toBeDisabled() - }) + expect(queryByLabelText('test input')).toBeInTheDocument(); + expect(queryByLabelText('test input')).not.toHaveAttribute('disabled'); + expect(queryByLabelText('test input')).toHaveAttribute('aria-disabled', 'false'); + expect(queryByLabelText('test input')).not.toBeDisabled(); + }); it('should have the `border-base` class by default', () => { - const { queryByLabelText } = render() + const { queryByLabelText } = render(); - expect(queryByLabelText('test input')).toBeInTheDocument() - expect(queryByLabelText('test input')).toHaveClass('border-base') - }) + expect(queryByLabelText('test input')).toBeInTheDocument(); + expect(queryByLabelText('test input')).toHaveClass('border-base'); + }); it('should have the `pl-12` class when the `icon` variant is passed', () => { - const { queryByLabelText } = render(} />) + const { queryByLabelText } = render(} />); - expect(queryByLabelText('test input')).toBeInTheDocument() - expect(queryByLabelText('test input')).toHaveClass('pl-12') - }) + expect(queryByLabelText('test input')).toBeInTheDocument(); + expect(queryByLabelText('test input')).toHaveClass('pl-12'); + }); it('should have the `bg-disabled` and `border-transparent` classes when the `disabled` variant is passed', () => { - const { queryByLabelText } = render() + const { queryByLabelText } = render(); - expect(queryByLabelText('test input')).toBeInTheDocument() - expect(queryByLabelText('test input')).toHaveClass('bg-disabled') - expect(queryByLabelText('test input')).toHaveClass('border-transparent') - }) + expect(queryByLabelText('test input')).toBeInTheDocument(); + expect(queryByLabelText('test input')).toHaveClass('bg-disabled'); + expect(queryByLabelText('test input')).toHaveClass('border-transparent'); + }); it('should have the `aria-invalid` and `aria-describedby` attributes if errorMessage is present', () => { const { queryByLabelText, queryByText } = render( , - ) - - expect(queryByLabelText('test input')).toBeInTheDocument() - expect(queryByLabelText('test input')).toHaveAttribute('aria-invalid') - expect(queryByLabelText('test input')).toHaveAttribute('aria-describedby') - expect(queryByText('some error')).toBeInTheDocument() - }) -}) + ); + + expect(queryByLabelText('test input')).toBeInTheDocument(); + expect(queryByLabelText('test input')).toHaveAttribute('aria-invalid'); + expect(queryByLabelText('test input')).toHaveAttribute('aria-describedby'); + expect(queryByText('some error')).toBeInTheDocument(); + }); +}); diff --git a/packages/extension/src/components/__tests__/radio-group.test.tsx b/packages/extension/src/components/__tests__/radio-group.test.tsx new file mode 100644 index 00000000..41cdfb5d --- /dev/null +++ b/packages/extension/src/components/__tests__/radio-group.test.tsx @@ -0,0 +1,106 @@ +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; + +import { Radio, RadioGroup } from '../radio-group'; + +describe('RadioGroup', () => { + const radioItems = [ + { label: 'Option 1', value: 'option1', checked: true }, + { label: 'Option 2', value: 'option2' }, + ]; + + it('should have the `flex-row` class when the `inline` variant is passed', () => { + const { queryByRole } = render( + , + ); + + expect(queryByRole('radiogroup')).toBeInTheDocument(); + expect(queryByRole('radiogroup')).toHaveClass('flex-row'); + }); + + it('renders radio group correctly with items', () => { + const { getByRole } = render(); + + const radioGroup = getByRole('radiogroup'); + expect(radioGroup).toBeInTheDocument(); + expect(radioGroup.childNodes.length).toBe(2); // Ensure two radio buttons are rendered + }); + + it('renders radio group with no element checked by default', () => { + const radioItemsNotChecked = [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + ]; + const { getByLabelText } = render( + , + ); + + const firstRadioButton = getByLabelText('Option 1'); + const secondRadioButton = getByLabelText('Option 2'); + + expect(firstRadioButton).not.toBeChecked(); + expect(secondRadioButton).not.toBeChecked(); + }); + + it('handles keyboard navigation', () => { + const { getByLabelText } = render(); + + const radioGroup = getByLabelText('Option 1'); + fireEvent.keyDown(radioGroup, { key: 'ArrowRight', code: 'ArrowRight' }); + let secondRadioButton = getByLabelText('Option 2'); + expect(secondRadioButton).toBeChecked(); + + fireEvent.keyDown(radioGroup, { key: 'ArrowLeft', code: 'ArrowLeft' }); + let firstRadioButton = getByLabelText('Option 1'); + expect(firstRadioButton).toBeChecked(); + + fireEvent.keyDown(radioGroup, { key: 'ArrowUp', code: 'ArrowUp' }); + secondRadioButton = getByLabelText('Option 2'); + expect(secondRadioButton).toBeChecked(); + + fireEvent.keyDown(radioGroup, { key: 'ArrowDown', code: 'ArrowDown' }); + firstRadioButton = getByLabelText('Option 1'); + expect(firstRadioButton).toBeChecked(); + }); + + it('changes selection on arrow keys', () => { + const { getByLabelText } = render(); + + const radioGroup = getByLabelText('Option 1'); + fireEvent.keyDown(radioGroup, { key: 'ArrowRight', code: 'ArrowRight' }); + fireEvent.keyDown(radioGroup, { key: 'Enter', code: 'Enter' }); + const secondRadioButton = getByLabelText('Option 2'); + expect(secondRadioButton).toBeChecked(); + }); + + it('changes selection on clicking radio buttons', () => { + const { getByLabelText } = render(); + + const secondRadioButton = getByLabelText('Option 2'); + fireEvent.click(secondRadioButton); + expect(secondRadioButton).toBeChecked(); + }); +}); + +describe('Radio', () => { + it('renders radio button correctly with label', () => { + const { getByLabelText } = render(); + + const radioButton = getByLabelText('Option 1'); + expect(radioButton).toBeInTheDocument(); + expect(radioButton).toHaveAttribute('type', 'radio'); + expect(radioButton).not.toBeChecked(); + + fireEvent.click(radioButton); + expect(radioButton).toBeChecked(); + }); + + it('renders disabled radio button', () => { + const { getByLabelText } = render( + , + ); + + const radioButton = getByLabelText('Option 1'); + expect(radioButton).toBeDisabled(); + }); +}); diff --git a/packages/extension/src/components/__tests__/switch.test.tsx b/packages/extension/src/components/__tests__/switch.test.tsx new file mode 100644 index 00000000..259a0d72 --- /dev/null +++ b/packages/extension/src/components/__tests__/switch.test.tsx @@ -0,0 +1,69 @@ +import '@testing-library/jest-dom'; + +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +import { Switch } from '@/components/switch'; + +describe('Switch', () => { + it('renders without crashing', () => { + render(); + expect(screen.getByRole('switch')).toBeInTheDocument(); + }); + + it('applies default classes', () => { + render(); + const switchElement = screen.getByRole('switch').nextSibling; + expect(switchElement).toHaveClass('w-[42px] h-[26px] before:h-5 before:w-5'); + }); + + it('applies small size classes when size prop is small', () => { + render(); + const switchElement = screen.getByRole('switch').nextSibling; + expect(switchElement).toHaveClass('w-9 h-[22px] before:h-4 before:w-4 before:left-[3px]'); + }); + + it('forwards ref to input element', () => { + const ref = React.createRef(); + render(); + expect(ref.current).toBeInstanceOf(HTMLInputElement); + }); + + it('forwards checked prop to input element', () => { + render(); + const inputElement = screen.getByRole('switch'); + expect(inputElement).toBeChecked(); + }); + + it('handles additional props', () => { + render(); + const inputElement = screen.getByRole('switch'); + expect(inputElement).toHaveAttribute('aria-label', 'Custom Switch'); + }); + + it('applies custom class names', () => { + const customClass = 'custom-class'; + render(); + const switchElement = screen.getByRole('switch').nextSibling; + expect(switchElement).toHaveClass(customClass); + }); + + it('toggles switch state when clicked', () => { + render(); + const inputElement = screen.getByRole('switch'); + expect(inputElement).not.toBeChecked(); + + fireEvent.click(inputElement); + expect(inputElement).toBeChecked(); + + fireEvent.click(inputElement); + expect(inputElement).not.toBeChecked(); + }); + + it('handles additional HTML attributes', () => { + const testId = 'switch-test'; + render(); + const switchElement = screen.getByTestId(testId); + expect(switchElement).toBeInTheDocument(); + }); +}); diff --git a/src/components/button.tsx b/packages/extension/src/components/button.tsx similarity index 86% rename from src/components/button.tsx rename to packages/extension/src/components/button.tsx index 4566fc04..6167d342 100644 --- a/src/components/button.tsx +++ b/packages/extension/src/components/button.tsx @@ -1,8 +1,8 @@ -import { type VariantProps, cva } from 'class-variance-authority' -import React, { forwardRef } from 'react' +import { type VariantProps, cva } from 'class-variance-authority'; +import React, { forwardRef } from 'react'; -import { LoadingSpinner } from '@/components/loading-spinner' -import { cn } from '@/utils/cn' +import { LoadingSpinner } from '@/components/loading-spinner'; +import { cn } from '@/utils/cn'; const buttonVariants = cva( [ @@ -32,13 +32,13 @@ const buttonVariants = cva( size: 'default', }, }, -) +); export interface ButtonProps extends VariantProps, React.ButtonHTMLAttributes { - loading?: boolean - ['aria-label']: string + loading?: boolean; + ['aria-label']: string; } export const Button = forwardRef(function Button( @@ -52,8 +52,9 @@ export const Button = forwardRef(function Button className={cn(buttonVariants({ variant, size, fullWidth, loading }), className)} disabled={props.disabled ?? loading ?? false} aria-disabled={props.disabled ?? loading ?? false} - {...props}> + {...props} + > {loading ? : children} - ) -}) + ); +}); diff --git a/src/components/icons.tsx b/packages/extension/src/components/icons.tsx similarity index 96% rename from src/components/icons.tsx rename to packages/extension/src/components/icons.tsx index 11950a58..ddcbf0d0 100644 --- a/src/components/icons.tsx +++ b/packages/extension/src/components/icons.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React from 'react'; export const Spinner = (props: React.SVGProps) => { return ( @@ -7,7 +7,8 @@ export const Spinner = (props: React.SVGProps) => { xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" - {...props}> + {...props} + > ) => { d="M2.204 6.447A6 6 0 108 2" /> - ) -} + ); +}; export const ArrowBack = (props: React.SVGProps) => { return ( @@ -26,7 +27,8 @@ export const ArrowBack = (props: React.SVGProps) => { viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg" - {...props}> + {...props} + > @@ -37,8 +39,8 @@ export const ArrowBack = (props: React.SVGProps) => { /> - ) -} + ); +}; export const Settings = (props: React.SVGProps) => { return ( @@ -48,7 +50,8 @@ export const Settings = (props: React.SVGProps) => { height="25" viewBox="0 0 25 25" fill="none" - {...props}> + {...props} + > @@ -59,8 +62,8 @@ export const Settings = (props: React.SVGProps) => { /> - ) -} + ); +}; export const DollarSign = (props: React.SVGProps) => { return ( @@ -70,7 +73,8 @@ export const DollarSign = (props: React.SVGProps) => { viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" - {...props}> + {...props} + > ) => { x="0" y="0" width="24" - height="24"> + height="24" + > @@ -91,5 +96,5 @@ export const DollarSign = (props: React.SVGProps) => { - ) -} + ); +}; diff --git a/src/components/input.tsx b/packages/extension/src/components/input.tsx similarity index 88% rename from src/components/input.tsx rename to packages/extension/src/components/input.tsx index c98ec04e..b25789d0 100644 --- a/src/components/input.tsx +++ b/packages/extension/src/components/input.tsx @@ -1,7 +1,7 @@ -import { type VariantProps, cva } from 'class-variance-authority' -import React, { forwardRef } from 'react' +import { type VariantProps, cva } from 'class-variance-authority'; +import React, { forwardRef } from 'react'; -import { cn } from '@/utils/cn' +import { cn } from '@/utils/cn'; const inputVariants = cva( [ @@ -23,14 +23,14 @@ const inputVariants = cva( variant: 'default', }, }, -) +); export interface InputProps extends VariantProps, React.InputHTMLAttributes { - errorMessage?: string - disabled?: boolean - icon?: React.ReactNode + errorMessage?: string; + disabled?: boolean; + icon?: React.ReactNode; } export const Input = forwardRef(function Input( @@ -52,5 +52,5 @@ export const Input = forwardRef(function Input( /> {errorMessage &&

{errorMessage}

} - ) -}) + ); +}); diff --git a/src/components/label.tsx b/packages/extension/src/components/label.tsx similarity index 77% rename from src/components/label.tsx rename to packages/extension/src/components/label.tsx index 94f6063f..b3621174 100644 --- a/src/components/label.tsx +++ b/packages/extension/src/components/label.tsx @@ -1,14 +1,14 @@ -import { type VariantProps, cva } from 'class-variance-authority' -import React, { forwardRef } from 'react' +import { type VariantProps, cva } from 'class-variance-authority'; +import React, { forwardRef } from 'react'; -import { cn } from '@/utils/cn' +import { cn } from '@/utils/cn'; -const labelVariants = cva('text-medium font-medium leading-6 px-2 flex items-center gap-2') +const labelVariants = cva('text-medium font-medium leading-6 px-2 flex items-center gap-2'); export interface LabelProps extends VariantProps, React.LabelHTMLAttributes { - children: React.ReactNode + children: React.ReactNode; } export const Label = forwardRef(function Label( @@ -19,5 +19,5 @@ export const Label = forwardRef(function Label( - ) -}) + ); +}); diff --git a/src/components/layout/header.tsx b/packages/extension/src/components/layout/header.tsx similarity index 68% rename from src/components/layout/header.tsx rename to packages/extension/src/components/layout/header.tsx index 77b84fb5..3b9ce42f 100644 --- a/src/components/layout/header.tsx +++ b/packages/extension/src/components/layout/header.tsx @@ -1,14 +1,14 @@ -import React, { useMemo } from 'react' -import { Link, useLocation } from 'react-router-dom' -import { runtime } from 'webextension-polyfill' +import React, { useMemo } from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { runtime } from 'webextension-polyfill'; -import { ArrowBack, Settings } from '../icons' -import { ROUTES } from '../router-provider' +import { ArrowBack, Settings } from '../icons'; +import { ROUTES } from '../router-provider'; -const Logo = runtime.getURL('assets/images/logo.svg') +const Logo = runtime.getURL('assets/images/logo.svg'); const NavigationButton = () => { - const location = useLocation() + const location = useLocation(); const component = useMemo( () => @@ -23,10 +23,10 @@ const NavigationButton = () => { ), [location], - ) + ); - return component -} + return component; +}; export const Header = () => { return ( @@ -39,5 +39,5 @@ export const Header = () => { - ) -} + ); +}; diff --git a/src/components/layout/main-layout.tsx b/packages/extension/src/components/layout/main-layout.tsx similarity index 56% rename from src/components/layout/main-layout.tsx rename to packages/extension/src/components/layout/main-layout.tsx index 92fabac9..7c53174a 100644 --- a/src/components/layout/main-layout.tsx +++ b/packages/extension/src/components/layout/main-layout.tsx @@ -1,11 +1,11 @@ -import React from 'react' -import { Outlet } from 'react-router-dom' +import React from 'react'; +import { Outlet } from 'react-router-dom'; -import { Header } from './header' +import { Header } from './header'; const Divider = () => { - return
-} + return
; +}; export const MainLayout = () => { return ( @@ -16,5 +16,5 @@ export const MainLayout = () => {
- ) -} + ); +}; diff --git a/src/components/loading-spinner.tsx b/packages/extension/src/components/loading-spinner.tsx similarity index 81% rename from src/components/loading-spinner.tsx rename to packages/extension/src/components/loading-spinner.tsx index c42a723c..d0d6f4b9 100644 --- a/src/components/loading-spinner.tsx +++ b/packages/extension/src/components/loading-spinner.tsx @@ -1,7 +1,7 @@ -import { type VariantProps, cva } from 'class-variance-authority' -import React from 'react' +import { type VariantProps, cva } from 'class-variance-authority'; +import React from 'react'; -import { Spinner } from '@/components/icons' +import { Spinner } from '@/components/icons'; const loadingSpinnerStyles = cva('animate-spin text-white', { variants: { @@ -13,10 +13,10 @@ const loadingSpinnerStyles = cva('animate-spin text-white', { defaultVariants: { variant: 'lg', }, -}) +}); -export type LoadingIndicatorProps = VariantProps +export type LoadingIndicatorProps = VariantProps; export const LoadingSpinner = ({ variant }: LoadingIndicatorProps) => { - return -} + return ; +}; diff --git a/src/components/radio-group.tsx b/packages/extension/src/components/radio-group.tsx similarity index 74% rename from src/components/radio-group.tsx rename to packages/extension/src/components/radio-group.tsx index 862cc0a4..b6db2594 100644 --- a/src/components/radio-group.tsx +++ b/packages/extension/src/components/radio-group.tsx @@ -1,17 +1,17 @@ -import { type VariantProps, cva } from 'class-variance-authority' -import React, { useEffect, useMemo, useState } from 'react' +import { type VariantProps, cva } from 'class-variance-authority'; +import React, { useEffect, useMemo, useState } from 'react'; -import { cn } from '@/utils/cn' +import { cn } from '@/utils/cn'; export interface RadioProps { - checked?: boolean - label?: string - value: string - name: string - id?: string - disabled?: boolean - onChange?: any - noSelected?: boolean + checked?: boolean; + label?: string; + value: string; + name: string; + id?: string; + disabled?: boolean; + onChange?: any; + noSelected?: boolean; } export const Radio = ({ @@ -24,12 +24,12 @@ export const Radio = ({ checked, noSelected, }: RadioProps): JSX.Element => { - const inputId = id || `id-${name}-${value}` - const divId = `div-${inputId}` + const inputId = id || `id-${name}-${value}`; + const divId = `div-${inputId}`; useEffect(() => { - if (checked) document.getElementById(divId)?.focus() - }, [checked, divId]) + if (checked) document.getElementById(divId)?.focus(); + }, [checked, divId]); return (
+ role="radio" + > {label}

: ''}
- ) -} + ); +}; const radioGroupVariants = cva(['flex gap-3'], { variants: { @@ -78,14 +79,14 @@ const radioGroupVariants = cva(['flex gap-3'], { defaultVariants: { variant: 'default', }, -}) +}); export interface RadioGroupProps extends VariantProps, React.InputHTMLAttributes { - disabled?: boolean - items: Omit[] - name: string + disabled?: boolean; + items: Omit[]; + name: string; } export const RadioGroup = ({ @@ -96,42 +97,43 @@ export const RadioGroup = ({ disabled, className, }: RadioGroupProps) => { - const checkedItem = useMemo(() => items.findIndex(item => item.checked), [items]) - const [selected, setSelected] = useState(checkedItem) + const checkedItem = useMemo(() => items.findIndex(item => item.checked), [items]); + const [selected, setSelected] = useState(checkedItem); const handleKeyDown = (event: React.KeyboardEvent) => { if (event.code === 'ArrowRight' || event.code === 'ArrowDown') { - event.preventDefault() + event.preventDefault(); - const nextIndex = (selected >= 0 ? selected + 1 : 1) % items.length - setSelected(nextIndex) + const nextIndex = (selected >= 0 ? selected + 1 : 1) % items.length; + setSelected(nextIndex); } else if (event.code === 'ArrowLeft' || event.code === 'ArrowUp') { - event.preventDefault() + event.preventDefault(); - const prevIndex = selected > 0 ? selected - 1 : items.length - 1 - setSelected(prevIndex) + const prevIndex = selected > 0 ? selected - 1 : items.length - 1; + setSelected(prevIndex); } - } + }; useEffect(() => { const handleKeyPress = (event: KeyboardEvent) => { if (selected === -1 && (event.code === 'Enter' || event.code === 'Space')) { - setSelected(0) + setSelected(0); } - } + }; - document.addEventListener('keypress', handleKeyPress) + document.addEventListener('keypress', handleKeyPress); return () => { - document.removeEventListener('keypress', handleKeyPress) - } - }, [selected]) + document.removeEventListener('keypress', handleKeyPress); + }; + }, [selected]); return (
+ role="radiogroup" + > {items.map((item, index) => ( ))}
- ) -} + ); +}; diff --git a/src/components/router-provider.tsx b/packages/extension/src/components/router-provider.tsx similarity index 65% rename from src/components/router-provider.tsx rename to packages/extension/src/components/router-provider.tsx index 076bcd74..7f6a083e 100644 --- a/src/components/router-provider.tsx +++ b/packages/extension/src/components/router-provider.tsx @@ -1,14 +1,14 @@ /* eslint-disable simple-import-sort/imports */ -import React from 'react' -import { MemoryRouter, Routes, Route } from 'react-router-dom' -import { Home } from '@/popup/pages/Home' -import { Settings } from '@/popup/pages/Settings' -import { MainLayout } from './layout/main-layout' +import React from 'react'; +import { MemoryRouter, Routes, Route } from 'react-router-dom'; +import { Home } from '@/popup/pages/Home'; +import { Settings } from '@/popup/pages/Settings'; +import { MainLayout } from './layout/main-layout'; export const ROUTES = { INDEX: 'index', SETTINGS: 'settings', -} +}; export const RouterProvider = () => ( @@ -19,4 +19,4 @@ export const RouterProvider = () => ( -) +); diff --git a/src/components/slider.tsx b/packages/extension/src/components/slider.tsx similarity index 85% rename from src/components/slider.tsx rename to packages/extension/src/components/slider.tsx index 2e8b454f..65b99203 100644 --- a/src/components/slider.tsx +++ b/packages/extension/src/components/slider.tsx @@ -1,22 +1,22 @@ -import React, { forwardRef } from 'react' +import React, { forwardRef } from 'react'; -import { cn } from '@/utils/cn' +import { cn } from '@/utils/cn'; export interface SliderProps extends React.InputHTMLAttributes { - errorMessage?: string - disabled?: boolean - icon?: React.ReactNode - min?: number - max?: number - value?: number - defaultValue?: number + errorMessage?: string; + disabled?: boolean; + icon?: React.ReactNode; + min?: number; + max?: number; + value?: number; + defaultValue?: number; } export const Slider = forwardRef(function Slider( { errorMessage, defaultValue, value, className, disabled, ...props }, ref, ) { - const [innerValue, setInnerValue] = React.useState(value || defaultValue || 0) + const [innerValue, setInnerValue] = React.useState(value || defaultValue || 0); return (
@@ -53,5 +53,5 @@ export const Slider = forwardRef(function Slider( {errorMessage &&

{errorMessage}

}
- ) -}) + ); +}); diff --git a/src/components/switch.tsx b/packages/extension/src/components/switch.tsx similarity index 92% rename from src/components/switch.tsx rename to packages/extension/src/components/switch.tsx index 5771072c..65a52dbf 100644 --- a/src/components/switch.tsx +++ b/packages/extension/src/components/switch.tsx @@ -1,7 +1,7 @@ -import { type VariantProps, cva } from 'class-variance-authority' -import React, { forwardRef } from 'react' +import { type VariantProps, cva } from 'class-variance-authority'; +import React, { forwardRef } from 'react'; -import { cn } from '@/utils/cn' +import { cn } from '@/utils/cn'; const switchVariants = cva( [ @@ -27,12 +27,12 @@ const switchVariants = cva( size: 'default', }, }, -) +); export interface SwitchProps extends VariantProps, React.HTMLAttributes { - checked?: boolean + checked?: boolean; } export const Switch = forwardRef(function Switch( @@ -50,5 +50,5 @@ export const Switch = forwardRef(function Switch( />
- ) -}) + ); +}); diff --git a/packages/extension/src/content/index.tsx b/packages/extension/src/content/index.tsx new file mode 100755 index 00000000..fe96294f --- /dev/null +++ b/packages/extension/src/content/index.tsx @@ -0,0 +1,26 @@ +import { runtime } from 'webextension-polyfill'; + +import { initMonetizationTagManager } from '@/utils/monetizationTagManager'; +import { wm2Polyfill } from '@/utils/polyfill'; + +import { loadObserver } from './linksObserver'; +import MessageListener from './messageListener'; + +// import "./content.css"; + +runtime.onMessage.addListener(MessageListener); + +function inject(configure: (_script: HTMLScriptElement) => void) { + const script = document.createElement('script'); + configure(script); + document.documentElement.appendChild(script); + // document.documentElement.removeChild(script) +} + +// eslint-disable-next-line @typescript-eslint/no-extra-semi +(function injectCode(code: string) { + inject(script => (script.innerHTML = code)); +})(wm2Polyfill); + +loadObserver(); +initMonetizationTagManager(); diff --git a/src/content/linksObserver.ts b/packages/extension/src/content/linksObserver.ts similarity index 74% rename from src/content/linksObserver.ts rename to packages/extension/src/content/linksObserver.ts index 727f871c..fadf6f2b 100644 --- a/src/content/linksObserver.ts +++ b/packages/extension/src/content/linksObserver.ts @@ -7,38 +7,38 @@ const listenForLinkChange = (mutationsList: MutationRecord[]) => { link.getAttribute('href')?.match(/^http/) && link.getAttribute('rel')?.match(/monetization/) ) { - acc.push(link) + acc.push(link); } - return acc + return acc; }, [], - ) + ); if (monetizationLinks.length) { - console.log(monetizationLinks) + console.log(monetizationLinks); } } if (mutationsList[0].type === 'attributes') { - const target = mutationsList[0].target as HTMLElement + const target = mutationsList[0].target as HTMLElement; if ( target.tagName?.toLowerCase() === 'link' && target.getAttribute('href')?.match(/^http/) && target.getAttribute('rel')?.match(/monetization/) ) { - console.log('LINK ATTR CHANGED', target) + console.log('LINK ATTR CHANGED', target); } } -} +}; export const loadObserver = () => { - const observer = new MutationObserver(listenForLinkChange) + const observer = new MutationObserver(listenForLinkChange); const observeOptions = { attributes: true, childList: true, subtree: true, - } + }; - observer.observe(document, observeOptions) -} + observer.observe(document, observeOptions); +}; diff --git a/src/content/messageListener.ts b/packages/extension/src/content/messageListener.ts similarity index 81% rename from src/content/messageListener.ts rename to packages/extension/src/content/messageListener.ts index e61fed35..1142ea89 100644 --- a/src/content/messageListener.ts +++ b/packages/extension/src/content/messageListener.ts @@ -1,8 +1,8 @@ // import { Runtime } from 'webextension-polyfill' -import { PaymentSender } from '@/content/monetization' +import { PaymentSender } from '@/content/monetization'; -const paymentSender = new PaymentSender() +const paymentSender = new PaymentSender(); export const onRequest = async ( msg: EXTMessage, @@ -12,7 +12,7 @@ export const onRequest = async ( switch (msg.type) { case 'IS_MONETIZATION_READY': { - const monetizationTag = document.querySelector('link[rel="monetization"]') + const monetizationTag = document.querySelector('link[rel="monetization"]'); return { type: 'SUCCESS', @@ -20,21 +20,21 @@ export const onRequest = async ( monetization: !!monetizationTag, paymentPointer: monetizationTag?.getAttribute('href'), }, - } + }; } case 'START_PAYMENTS': { - paymentSender.start() - break + paymentSender.start(); + break; } case 'STOP_PAYMENTS': { - paymentSender.stop() - break + paymentSender.stop(); + break; } case 'PAYMENT_SUCCESS': { - const { receiveAmount, incomingPayment, paymentPointer } = msg.data + const { receiveAmount, incomingPayment, paymentPointer } = msg.data; window.dispatchEvent( new CustomEvent('monetization-v2', { @@ -51,14 +51,14 @@ export const onRequest = async ( receipt: null, }, }), - ) + ); - break + break; } default: - return { type: 'SUCCESS' } + return { type: 'SUCCESS' }; } -} +}; -export default onRequest +export default onRequest; diff --git a/src/content/monetization.tsx b/packages/extension/src/content/monetization.tsx similarity index 51% rename from src/content/monetization.tsx rename to packages/extension/src/content/monetization.tsx index c7043939..fb256556 100644 --- a/src/content/monetization.tsx +++ b/packages/extension/src/content/monetization.tsx @@ -1,12 +1,12 @@ -import { sendMessage } from '@/utils/sendMessages' +import { sendMessage } from '@/utils/sendMessages'; export class PaymentSender { - sender: any + sender: any; start() { this.sender = setInterval(() => { - this.send() - }, 1000) + this.send(); + }, 1000); // stop payments manually after 3 seconds // setTimeout(() => { @@ -15,11 +15,11 @@ export class PaymentSender { } stop() { - sendMessage({ type: 'PAUSE_PAYMENTS' }) - clearInterval(this.sender) + sendMessage({ type: 'PAUSE_PAYMENTS' }); + clearInterval(this.sender); } send() { - sendMessage({ type: 'RUN_PAYMENT' }) + sendMessage({ type: 'RUN_PAYMENT' }); } } diff --git a/packages/extension/src/manifest/chrome-v3.json b/packages/extension/src/manifest/chrome-v3.json new file mode 100644 index 00000000..60aa11ea --- /dev/null +++ b/packages/extension/src/manifest/chrome-v3.json @@ -0,0 +1,39 @@ +{ + "name": "__MSG_appName__", + "version": "1.0.1", + "manifest_version": 3, + "description": "__MSG_appDescription__", + "icons": { + "34": "assets/icons/icon-34.png", + "128": "assets/icons/icon-128.png" + }, + "default_locale": "en", + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*"], + "js": ["content/content.js"] + } + ], + "background": { + "service_worker": "background/background.js" + }, + "permissions": ["tabs", "storage"], + "host_permissions": ["http://*/*", "https://*/*"], + "options_ui": { + "page": "options/index.html" + }, + "action": { + "default_icon": { + "16": "assets/icons/icon-16.png", + "48": "assets/icons/icon-48.png" + }, + "default_title": "Web Monetization", + "default_popup": "popup/index.html" + }, + "web_accessible_resources": [ + { + "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], + "matches": [""] + } + ] +} diff --git a/src/manifest/chrome.json b/packages/extension/src/manifest/chrome.json similarity index 100% rename from src/manifest/chrome.json rename to packages/extension/src/manifest/chrome.json diff --git a/packages/extension/src/manifest/edge.json b/packages/extension/src/manifest/edge.json new file mode 100644 index 00000000..631cd6eb --- /dev/null +++ b/packages/extension/src/manifest/edge.json @@ -0,0 +1,27 @@ +{ + "name": "__MSG_appName__", + "version": "1.0.1", + "manifest_version": 3, + "description": "__MSG_appDescription__", + "icons": { + "34": "assets/icons/icon-34.png", + "128": "assets/icons/icon-128.png" + }, + "default_locale": "en", + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*"], + "js": ["content/content.js"] + } + ], + "background": { + "service_worker": "background/background.js" + }, + "host_permissions": ["http://*/*", "https://*/*"], + "web_accessible_resources": [ + { + "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], + "matches": [""] + } + ] +} diff --git a/packages/extension/src/manifest/firefox-v3.json b/packages/extension/src/manifest/firefox-v3.json new file mode 100644 index 00000000..a7b770aa --- /dev/null +++ b/packages/extension/src/manifest/firefox-v3.json @@ -0,0 +1,47 @@ +{ + "name": "__MSG_appName__", + "version": "1.0.1", + "manifest_version": 3, + "description": "__MSG_appDescription__", + "icons": { + "34": "assets/icons/icon-34.png", + "128": "assets/icons/icon-128.png" + }, + "default_locale": "en", + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*"], + "js": ["content/content.js"] + } + ], + "background": { + "scripts": ["background/background.js"], + "type": "module" + }, + "permissions": ["tabs", "storage"], + "host_permissions": ["http://*/*", "https://*/*"], + "options_ui": { + "page": "options/index.html", + "browser_style": false + }, + "action": { + "default_icon": { + "16": "assets/icons/icon-16.png", + "48": "assets/icons/icon-48.png" + }, + "default_title": "Web Monetization", + "default_popup": "popup/index.html", + "browser_style": false + }, + "web_accessible_resources": [ + { + "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], + "matches": [""] + } + ], + "browser_specific_settings": { + "gecko": { + "id": "web-extension@interledger.com" + } + } +} diff --git a/packages/extension/src/manifest/firefox.json b/packages/extension/src/manifest/firefox.json new file mode 100644 index 00000000..275b1555 --- /dev/null +++ b/packages/extension/src/manifest/firefox.json @@ -0,0 +1,34 @@ +{ + "name": "__MSG_appName__", + "version": "1.0.1", + "manifest_version": 2, + "description": "__MSG_appDescription__", + "icons": { + "34": "assets/icons/icon-34.png", + "128": "assets/icons/icon-128.png" + }, + "default_locale": "en", + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*", ""], + "js": ["content/content.js"] + } + ], + "background": { + "scripts": ["background/background.js"], + "type": "module" + }, + "permissions": ["tabs", "storage"], + "browser_action": { + "default_icon": "assets/icons/icon-34.png", + "default_title": "Web Monetization", + "default_popup": "popup/index.html", + "browser_style": false + }, + "web_accessible_resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], + "browser_specific_settings": { + "gecko": { + "id": "web-extension@interledger.com" + } + } +} diff --git a/packages/extension/src/manifest/opera.json b/packages/extension/src/manifest/opera.json new file mode 100644 index 00000000..631cd6eb --- /dev/null +++ b/packages/extension/src/manifest/opera.json @@ -0,0 +1,27 @@ +{ + "name": "__MSG_appName__", + "version": "1.0.1", + "manifest_version": 3, + "description": "__MSG_appDescription__", + "icons": { + "34": "assets/icons/icon-34.png", + "128": "assets/icons/icon-128.png" + }, + "default_locale": "en", + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*"], + "js": ["content/content.js"] + } + ], + "background": { + "service_worker": "background/background.js" + }, + "host_permissions": ["http://*/*", "https://*/*"], + "web_accessible_resources": [ + { + "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], + "matches": [""] + } + ] +} diff --git a/src/popup/Popup.scss b/packages/extension/src/popup/Popup.scss similarity index 100% rename from src/popup/Popup.scss rename to packages/extension/src/popup/Popup.scss diff --git a/packages/extension/src/popup/Popup.tsx b/packages/extension/src/popup/Popup.tsx new file mode 100644 index 00000000..895fb064 --- /dev/null +++ b/packages/extension/src/popup/Popup.tsx @@ -0,0 +1,11 @@ +import './Popup.scss'; + +import React from 'react'; + +import { RouterProvider } from '@/components/router-provider'; + +const Popup = () => { + return ; +}; + +export default Popup; diff --git a/src/popup/index.css b/packages/extension/src/popup/index.css similarity index 100% rename from src/popup/index.css rename to packages/extension/src/popup/index.css diff --git a/packages/extension/src/popup/index.html b/packages/extension/src/popup/index.html new file mode 100755 index 00000000..42644b2c --- /dev/null +++ b/packages/extension/src/popup/index.html @@ -0,0 +1,10 @@ + + + + <%= htmlWebpackPlugin.options.title %> + + + + + + diff --git a/packages/extension/src/popup/index.tsx b/packages/extension/src/popup/index.tsx new file mode 100755 index 00000000..26d956bc --- /dev/null +++ b/packages/extension/src/popup/index.tsx @@ -0,0 +1,9 @@ +import './index.css'; + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import Popup from './Popup'; + +const root = ReactDOM.createRoot(document.getElementById('popup-container')!); +root.render(); diff --git a/src/popup/pages/Home.tsx b/packages/extension/src/popup/pages/Home.tsx similarity index 75% rename from src/popup/pages/Home.tsx rename to packages/extension/src/popup/pages/Home.tsx index 46a81f0e..9098249e 100644 --- a/src/popup/pages/Home.tsx +++ b/packages/extension/src/popup/pages/Home.tsx @@ -1,18 +1,18 @@ -import React, { useEffect, useState } from 'react' -import { runtime } from 'webextension-polyfill' +import React, { useEffect, useState } from 'react'; +import { runtime } from 'webextension-polyfill'; -import { sendMessage, sendMessageToActiveTab } from '@/utils/sendMessages' +import { sendMessage, sendMessageToActiveTab } from '@/utils/sendMessages'; -const Success = runtime.getURL('assets/images/web-monetization-success.svg') -const Fail = runtime.getURL('assets/images/web-monetization-fail.svg') -const CheckIcon = runtime.getURL('assets/images/check.svg') -const DollarIcon = runtime.getURL('assets/images/dollar.svg') -const CloseIcon = runtime.getURL('assets/images/close.svg') +const Success = runtime.getURL('assets/images/web-monetization-success.svg'); +const Fail = runtime.getURL('assets/images/web-monetization-fail.svg'); +const CheckIcon = runtime.getURL('assets/images/check.svg'); +const DollarIcon = runtime.getURL('assets/images/dollar.svg'); +const CloseIcon = runtime.getURL('assets/images/close.svg'); // --- Temporary code until real UI implemented --- interface IProps { - isMonetizationReady: boolean + isMonetizationReady: boolean; } const PopupFooter: React.FC = ({ isMonetizationReady }) => ( @@ -23,93 +23,93 @@ const PopupFooter: React.FC = ({ isMonetizationReady }) => ( This site isn't Web Monetization ready )} -) +); // --- End of Temporary code until real UI implemented --- export const Home = () => { - const [loading, setLoading] = useState(false) - const [paymentStarted, setPaymentStarted] = useState(false) - const [spent, setSpent] = useState(0) - const [sendingPaymentPointer, setSendingPaymentPointer] = useState('') - const [isMonetizationReady, setIsMonetizationReady] = useState(false) - const [receivingPaymentPointer, setReceivingPaymentPointer] = useState('') + const [loading, setLoading] = useState(false); + const [paymentStarted, setPaymentStarted] = useState(false); + const [spent, setSpent] = useState(0); + const [sendingPaymentPointer, setSendingPaymentPointer] = useState(''); + const [isMonetizationReady, setIsMonetizationReady] = useState(false); + const [receivingPaymentPointer, setReceivingPaymentPointer] = useState(''); const [formData, setFormData] = useState({ paymentPointer: sendingPaymentPointer || '', amount: 20, - }) + }); useEffect(() => { - checkMonetizationReady() - getSendingPaymentPointer() - listenForIncomingPayment() + checkMonetizationReady(); + getSendingPaymentPointer(); + listenForIncomingPayment(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + }, []); const checkMonetizationReady = async () => { - const response = await sendMessageToActiveTab({ type: 'IS_MONETIZATION_READY' }) - setIsMonetizationReady(response.data.monetization) - setReceivingPaymentPointer(response.data.paymentPointer) - } + const response = await sendMessageToActiveTab({ type: 'IS_MONETIZATION_READY' }); + setIsMonetizationReady(response.data.monetization); + setReceivingPaymentPointer(response.data.paymentPointer); + }; const handleChange = (event: React.ChangeEvent) => { - setFormData(prevState => ({ ...prevState, [event.target.name]: event.target.value })) - } + setFormData(prevState => ({ ...prevState, [event.target.name]: event.target.value })); + }; const handleSubmit = async (event: React.FormEvent) => { - event.preventDefault() + event.preventDefault(); - setLoading(true) + setLoading(true); const data = { amount: formData.amount, paymentPointer: formData.paymentPointer, incomingPayment: receivingPaymentPointer, - } + }; - await sendMessage({ type: 'SET_INCOMING_POINTER', data }) - } + await sendMessage({ type: 'SET_INCOMING_POINTER', data }); + }; const getSendingPaymentPointer = async () => { - const response = await sendMessage({ type: 'GET_SENDING_PAYMENT_POINTER' }) - setSendingPaymentPointer(response.data.sendingPaymentPointerUrl) + const response = await sendMessage({ type: 'GET_SENDING_PAYMENT_POINTER' }); + setSendingPaymentPointer(response.data.sendingPaymentPointerUrl); - const { sendingPaymentPointerUrl: paymentPointer, amount } = response.data + const { sendingPaymentPointerUrl: paymentPointer, amount } = response.data; if (paymentPointer && amount) { setFormData({ paymentPointer: response.data.sendingPaymentPointerUrl, amount: response.data.amount, - }) + }); } - } + }; const listenForIncomingPayment = async () => { const listener = (message: any) => { if (message.type === 'SPENT_AMOUNT') { - setSpent(message.data.spentAmount) - setPaymentStarted(true) + setSpent(message.data.spentAmount); + setPaymentStarted(true); } if (loading) { - setLoading(false) + setLoading(false); } - } + }; - runtime.onMessage.addListener(listener) + runtime.onMessage.addListener(listener); return () => { - runtime.onMessage.removeListener(listener) - } - } + runtime.onMessage.removeListener(listener); + }; + }; const stopPayments = async (e: React.MouseEvent) => { - e.preventDefault() - setPaymentStarted(false) + e.preventDefault(); + setPaymentStarted(false); setTimeout(() => { if (loading) { - setLoading(false) + setLoading(false); } - }, 1000) - await sendMessageToActiveTab({ type: 'STOP_PAYMENTS' }) - } + }, 1000); + await sendMessageToActiveTab({ type: 'STOP_PAYMENTS' }); + }; return ( <> @@ -125,7 +125,8 @@ export const Home = () => {
+ className={`pointerForm ${paymentStarted ? 'active' : ''}`} + >
@@ -174,5 +175,5 @@ export const Home = () => {
- ) -} + ); +}; diff --git a/packages/extension/src/popup/pages/Settings.tsx b/packages/extension/src/popup/pages/Settings.tsx new file mode 100644 index 00000000..36a60035 --- /dev/null +++ b/packages/extension/src/popup/pages/Settings.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export const Settings = () => { + return <>Settings; +}; diff --git a/src/types/message.d.ts b/packages/extension/src/types/message.d.ts similarity index 83% rename from src/types/message.d.ts rename to packages/extension/src/types/message.d.ts index 8dc5b546..021df720 100644 --- a/src/types/message.d.ts +++ b/packages/extension/src/types/message.d.ts @@ -9,9 +9,9 @@ declare type EXTMessageType = | 'START_PAYMENTS' | 'STOP_PAYMENTS' | 'PAYMENT_SUCCESS' - | 'PAUSE_PAYMENTS' + | 'PAUSE_PAYMENTS'; declare type EXTMessage = { - type: EXTMessageType - data?: T -} + type: EXTMessageType; + data?: T; +}; diff --git a/packages/extension/src/types/openControl.d.ts b/packages/extension/src/types/openControl.d.ts new file mode 100644 index 00000000..3a1bfab1 --- /dev/null +++ b/packages/extension/src/types/openControl.d.ts @@ -0,0 +1,7 @@ +declare type OpenControls = { + isVisible: boolean; + handleClose: () => void; + handleOpen: () => void; + handleToggle: () => void; + isOpen: boolean; +}; diff --git a/src/types/response.d.ts b/packages/extension/src/types/response.d.ts similarity index 57% rename from src/types/response.d.ts rename to packages/extension/src/types/response.d.ts index 6cf03154..40c356fc 100644 --- a/src/types/response.d.ts +++ b/packages/extension/src/types/response.d.ts @@ -1,6 +1,6 @@ -declare type EXTResponseType = 'SUCCESS' | 'FAILED' | 'PENDING' | 'UNAUTHORIZED' | 'AUTHENTICATED' +declare type EXTResponseType = 'SUCCESS' | 'FAILED' | 'PENDING' | 'UNAUTHORIZED' | 'AUTHENTICATED'; declare type EXTResponse = { - type: EXTResponseType - data?: T -} + type: EXTResponseType; + data?: T; +}; diff --git a/packages/extension/src/utils/cn.ts b/packages/extension/src/utils/cn.ts new file mode 100644 index 00000000..fbb7fea2 --- /dev/null +++ b/packages/extension/src/utils/cn.ts @@ -0,0 +1,6 @@ +import { cx, CxOptions } from 'class-variance-authority'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: CxOptions) { + return twMerge(cx(inputs)); +} diff --git a/src/utils/monetizationTagManager/CustomError.ts b/packages/extension/src/utils/monetizationTagManager/CustomError.ts similarity index 63% rename from src/utils/monetizationTagManager/CustomError.ts rename to packages/extension/src/utils/monetizationTagManager/CustomError.ts index b259ec2a..8702500f 100644 --- a/src/utils/monetizationTagManager/CustomError.ts +++ b/packages/extension/src/utils/monetizationTagManager/CustomError.ts @@ -1,16 +1,16 @@ export class CustomError extends Error { constructor(message?: string) { // 'Error' breaks prototype chain here - super(message) + super(message); // restore prototype chain - const actualProto = new.target.prototype + const actualProto = new.target.prototype; if (Object.setPrototypeOf) { - Object.setPrototypeOf(this, actualProto) + Object.setPrototypeOf(this, actualProto); } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(this as any).__proto__ = actualProto + (this as any).__proto__ = actualProto; } } } diff --git a/src/utils/monetizationTagManager/MonetizationTagManager.ts b/packages/extension/src/utils/monetizationTagManager/MonetizationTagManager.ts similarity index 67% rename from src/utils/monetizationTagManager/MonetizationTagManager.ts rename to packages/extension/src/utils/monetizationTagManager/MonetizationTagManager.ts index bf616c64..0f84bcb0 100644 --- a/src/utils/monetizationTagManager/MonetizationTagManager.ts +++ b/packages/extension/src/utils/monetizationTagManager/MonetizationTagManager.ts @@ -1,15 +1,15 @@ /* eslint-disable */ -import { EventEmitter } from 'events' -import { v4 as uuidV4 } from 'uuid' +import { EventEmitter } from 'events'; +import { v4 as uuidV4 } from 'uuid'; -import { CustomError } from './CustomError' -import { mozClone } from './mozClone' -import { resolvePaymentEndpoint } from './resolvePaymentEndpoint' -import { whenDocumentReady } from './whenDocumentReady' +import { CustomError } from './CustomError'; +import { mozClone } from './mozClone'; +import { resolvePaymentEndpoint } from './resolvePaymentEndpoint'; +import { whenDocumentReady } from './whenDocumentReady'; const debug = // console.log.bind(console, 'MonetizationTagManager') - (..._: unknown[]) => {} + (..._: unknown[]) => {}; export interface PaymentDetails { /** @@ -17,12 +17,12 @@ export interface PaymentDetails { * configuration of a tag. e.g. a new href on a link will mean a new * requestId */ - requestId: string - paymentPointer: string - initiatingUrl: string - fromBody: boolean - tagType: TagType - attrs: Record + requestId: string; + paymentPointer: string; + initiatingUrl: string; + fromBody: boolean; + tagType: TagType; + attrs: Record; } /** @@ -31,95 +31,95 @@ export interface PaymentDetails { * On a tag[content|href] changed, both will be set */ export interface PaymentDetailsChangeArguments { - started: PaymentDetails | null - stopped: PaymentDetails | null + started: PaymentDetails | null; + stopped: PaymentDetails | null; } -export type PaymentDetailsChangeCallback = (_args: PaymentDetailsChangeArguments) => void +export type PaymentDetailsChangeCallback = (_args: PaymentDetailsChangeArguments) => void; -export type MonetizationTag = HTMLMetaElement | HTMLLinkElement -export type MonetizationTagList = NodeListOf +export type MonetizationTag = HTMLMetaElement | HTMLLinkElement; +export type MonetizationTagList = NodeListOf; -export type TagType = 'meta' | 'link' +export type TagType = 'meta' | 'link'; export function getTagType(tag: MonetizationTag): TagType { - return tag instanceof HTMLMetaElement ? 'meta' : 'link' + return tag instanceof HTMLMetaElement ? 'meta' : 'link'; } export class DeprecatedMetaTagIgnoredError extends CustomError {} interface FireOnMonetizationChangeIfHaveAttributeParams { - node: HTMLElement - changeDetected?: boolean + node: HTMLElement; + changeDetected?: boolean; } export const metaDeprecatedMessage = 'Web-Monetization Error: ' + 'A `` tag has been seen, so ' + 'ignoring deprecated `` tag and ' + - 'using only `` tags consistently' + 'using only `` tags consistently'; export const MonetizationTagAttrs = { meta: ['content', 'name'], link: ['href', 'disabled', 'rel', 'crossorigin', 'type'], -} +}; -const MAX_NUMBER_META_TAGS = 1 +const MAX_NUMBER_META_TAGS = 1; function monetizationTagTypeSpecified(tag: Node, ambiguous = false): tag is MonetizationTag { if (tag instanceof HTMLLinkElement) { - return tag.rel === 'monetization' || (ambiguous && !tag.rel) + return tag.rel === 'monetization' || (ambiguous && !tag.rel); } else if (tag instanceof HTMLMetaElement) { - return tag.name === 'monetization' || (ambiguous && !tag.name) + return tag.name === 'monetization' || (ambiguous && !tag.name); } else { - return false + return false; } } function nodeIsPotentiallyMonetizationTag(node: Node): node is MonetizationTag { - return node instanceof HTMLLinkElement || node instanceof HTMLMetaElement + return node instanceof HTMLLinkElement || node instanceof HTMLMetaElement; } function getTagAttrs(tag: MonetizationTag, tagType: TagType) { return Object.fromEntries( MonetizationTagAttrs[tagType].map(attr => { - return [attr, tag.getAttribute(attr)] + return [attr, tag.getAttribute(attr)]; }), - ) + ); } export class MonetizationTagManager extends EventEmitter { - private affinity: TagType = 'meta' - private documentObserver: MutationObserver - private monetizationTagAttrObserver: MutationObserver + private affinity: TagType = 'meta'; + private documentObserver: MutationObserver; + private monetizationTagAttrObserver: MutationObserver; private monetizationTags = new Map< MonetizationTag, { - details: PaymentDetails + details: PaymentDetails; } - >() + >(); - private linkTagsById = new Map() + private linkTagsById = new Map(); dispatchEventByLinkId(id: string, event: Event) { - const link = this.linkTagsById.get(id) + const link = this.linkTagsById.get(id); if (link) { - debug('dispatchLinkEventByLinkId', id, event) - link.dispatchEvent(event) + debug('dispatchLinkEventByLinkId', id, event); + link.dispatchEvent(event); } } requestIds(): string[] { - return Array.from(this.monetizationTags.values()).map(e => e.details.requestId) + return Array.from(this.monetizationTags.values()).map(e => e.details.requestId); } linkRequests(): PaymentDetails[] { - return this.requests().filter(d => d.tagType === 'link') + return this.requests().filter(d => d.tagType === 'link'); } requests(): PaymentDetails[] { - return Array.from(this.monetizationTags.values()).map(e => e.details) + return Array.from(this.monetizationTags.values()).map(e => e.details); } constructor( @@ -128,81 +128,81 @@ export class MonetizationTagManager extends EventEmitter { private callback: PaymentDetailsChangeCallback, private throwOnIllegalState = true, ) { - super() - this.documentObserver = new MutationObserver(records => this._onWholeDocumentObserved(records)) + super(); + this.documentObserver = new MutationObserver(records => this._onWholeDocumentObserved(records)); this.monetizationTagAttrObserver = new MutationObserver(records => this._onMonetizationTagAttrsChange(records), - ) + ); } /** * Wait until the document is ready and formed */ startWhenDocumentReady(): void { - whenDocumentReady(this.document, this._start.bind(this)) + whenDocumentReady(this.document, this._start.bind(this)); } // Though this is `public`, it's not part of the public interface, so we // prefix this method with `_` with no `private` modifier, in order to // jest.spyOn it. _start() { - const monetizationTags: MonetizationTagList = this.document.querySelectorAll('meta,link') + const monetizationTags: MonetizationTagList = this.document.querySelectorAll('meta,link'); monetizationTags.forEach(tag => { try { - this._observeMonetizationTagAttrs(tag) - this.onAddedTag(tag) + this._observeMonetizationTagAttrs(tag); + this.onAddedTag(tag); } catch (e) { // eslint-disable-next-line no-console - console.error(e) + console.error(e); } - }) - const onMonetizations = this.document.querySelectorAll('[onmonetization]') + }); + const onMonetizations = this.document.querySelectorAll('[onmonetization]'); onMonetizations.forEach(om => { - this.checkMonetizationAttr(om) - }) + this.checkMonetizationAttr(om); + }); this.documentObserver.observe(this.document, { subtree: true, childList: true, attributeFilter: ['onmonetization'], - }) + }); } _check(op: string, node: Node) { - debug('head node', op, node) + debug('head node', op, node); if (nodeIsPotentiallyMonetizationTag(node)) { if (op === 'added') { - this._observeMonetizationTagAttrs(node) - this.onAddedTag(node) + this._observeMonetizationTagAttrs(node); + this.onAddedTag(node); } else if (op === 'removed' && this.monetizationTags.has(node)) { - this._onRemovedTag(node) + this._onRemovedTag(node); } } if (op === 'added' && node instanceof HTMLElement) { - this.checkMonetizationAttr(node) + this.checkMonetizationAttr(node); } } - _checkRemoved = this._check.bind(this, 'removed') - _checkAdded = this._check.bind(this, 'added') + _checkRemoved = this._check.bind(this, 'removed'); + _checkAdded = this._check.bind(this, 'added'); _onWholeDocumentObserved(records: MutationRecord[]) { - debug('document mutation records.length=', records.length) + debug('document mutation records.length=', records.length); // Explicitly remove these first for (const record of records) { - debug('Record', record.type, record.target) + debug('Record', record.type, record.target); if (record.type === 'childList') { - record.removedNodes.forEach(this._checkRemoved) + record.removedNodes.forEach(this._checkRemoved); } } for (const record of records) { - debug('Record', record.type, record.target) + debug('Record', record.type, record.target); if (record.type === 'childList') { - record.addedNodes.forEach(this._checkAdded) + record.addedNodes.forEach(this._checkAdded); } } - this.onOnMonetizationChangeObserved(records) + this.onOnMonetizationChangeObserved(records); } /** @@ -211,27 +211,27 @@ export class MonetizationTagManager extends EventEmitter { * @param records */ _onMonetizationTagAttrsChange(records: MutationRecord[]) { - const handledTags = new Set() + const handledTags = new Set(); // Check for a non specified link or meta with the type now specified and // just treat it as a newly seen, monetization tag for (const record of records) { - const target = record.target as MonetizationTag + const target = record.target as MonetizationTag; if (handledTags.has(target)) { - continue + continue; } - const hasTarget = this.monetizationTags.has(target) - const typeSpecified = monetizationTagTypeSpecified(target) + const hasTarget = this.monetizationTags.has(target); + const typeSpecified = monetizationTagTypeSpecified(target); // this will also handle the case of a @disabled tag that // is not tracked, becoming enabled if (!hasTarget && typeSpecified) { - this.onAddedTag(target) - handledTags.add(target) + this.onAddedTag(target); + handledTags.add(target); } else if (hasTarget && !typeSpecified) { - this._onRemovedTag(target) - handledTags.add(target) + this._onRemovedTag(target); + handledTags.add(target); } else if (!hasTarget && !typeSpecified) { // ignore these changes - handledTags.add(target) + handledTags.add(target); } else if (hasTarget && typeSpecified) { if ( record.type === 'attributes' && @@ -240,11 +240,11 @@ export class MonetizationTagManager extends EventEmitter { // can't use record.target[disabled] as it's a Boolean not string target.getAttribute('disabled') !== record.oldValue ) { - const wasDisabled = record.oldValue !== null - const isDisabled = target.hasAttribute('disabled') + const wasDisabled = record.oldValue !== null; + const isDisabled = target.hasAttribute('disabled'); if (wasDisabled != isDisabled) { - this._onChangedPaymentEndpoint(target, isDisabled, wasDisabled) - handledTags.add(target) + this._onChangedPaymentEndpoint(target, isDisabled, wasDisabled); + handledTags.add(target); } } else if ( record.type === 'attributes' && @@ -252,16 +252,16 @@ export class MonetizationTagManager extends EventEmitter { target instanceof HTMLMetaElement && target.content !== record.oldValue ) { - this._onChangedPaymentEndpoint(target) - handledTags.add(target) + this._onChangedPaymentEndpoint(target); + handledTags.add(target); } else if ( record.type === 'attributes' && record.attributeName === 'href' && target instanceof HTMLLinkElement && target.href !== record.oldValue ) { - this._onChangedPaymentEndpoint(target) - handledTags.add(target) + this._onChangedPaymentEndpoint(target); + handledTags.add(target); } } } @@ -280,36 +280,36 @@ export class MonetizationTagManager extends EventEmitter { */ private onAddedTag(tag: MonetizationTag) { if (!monetizationTagTypeSpecified(tag)) { - return + return; } - const type = getTagType(tag) + const type = getTagType(tag); // TODO:WM2 any other cases? if (type === 'link' && tag.hasAttribute('disabled')) { - return + return; } if (type != this.affinity) { if (type === 'link') { - this.affinity = 'link' + this.affinity = 'link'; // @ts-ignore for (const tag of this.monetizationTags.keys()) { - this._onRemovedTag(tag) + this._onRemovedTag(tag); } } } - let started: PaymentDetails | null = this.getPaymentDetails(tag) + let started: PaymentDetails | null = this.getPaymentDetails(tag); if (started.fromBody && started.tagType === 'meta') { const error = new Error( 'Web-Monetization Error: ' + 'must be in the document head', - ) - this.emit('illegal-state-error', error) + ); + this.emit('illegal-state-error', error); if (this.throwOnIllegalState) { - throw error + throw error; } else { - return + return; } } if ( @@ -330,92 +330,92 @@ export class MonetizationTagManager extends EventEmitter { // TODO:WM2, but this DOES work? // this won't work, may need to halt completely all the way up // the stack - return + return; } } if (started.tagType === 'link') { - started = this.checkStartedLinkForWellFormedness(started, tag) + started = this.checkStartedLinkForWellFormedness(started, tag); } if (started) { - this.monetizationTags.set(tag, { details: started }) - this.callback({ stopped: null, started: started }) + this.monetizationTags.set(tag, { details: started }); + this.callback({ stopped: null, started: started }); } } _observeMonetizationTagAttrs(tag: MonetizationTag) { - const attributeFilter = MonetizationTagAttrs[getTagType(tag)] + const attributeFilter = MonetizationTagAttrs[getTagType(tag)]; this.monetizationTagAttrObserver.observe(tag, { childList: false, attributeOldValue: true, attributeFilter, - }) + }); } _onRemovedTag(tag: MonetizationTag) { - const entry = this.getEntry(tag, '_onRemovedTag') - this.monetizationTags.delete(tag) - this.clearLinkById(entry.details) - this.callback({ started: null, stopped: entry.details }) + const entry = this.getEntry(tag, '_onRemovedTag'); + this.monetizationTags.delete(tag); + this.clearLinkById(entry.details); + this.callback({ started: null, stopped: entry.details }); } private getEntry(meta: MonetizationTag, caller = '') { - const entry = this.monetizationTags.get(meta) + const entry = this.monetizationTags.get(meta); if (!entry) { - throw new Error(`${caller}: tag not tracked: ${meta.outerHTML.slice(0, 200)}`) + throw new Error(`${caller}: tag not tracked: ${meta.outerHTML.slice(0, 200)}`); } - return entry + return entry; } _onChangedPaymentEndpoint(tag: MonetizationTag, disabled = false, wasDisabled = false) { - const entry = this.getEntry(tag, '_onChangedPaymentEndpoint') - const stopped = wasDisabled ? null : entry.details - this.clearLinkById(entry.details) - let started: PaymentDetails | null = null + const entry = this.getEntry(tag, '_onChangedPaymentEndpoint'); + const stopped = wasDisabled ? null : entry.details; + this.clearLinkById(entry.details); + let started: PaymentDetails | null = null; if (!disabled) { - started = this.getPaymentDetails(tag) - started = this.checkStartedLinkForWellFormedness(started, tag) + started = this.getPaymentDetails(tag); + started = this.checkStartedLinkForWellFormedness(started, tag); if (started) { - entry.details = started + entry.details = started; } } if (started || stopped) { - this.callback({ started, stopped }) + this.callback({ started, stopped }); } } private checkStartedLinkForWellFormedness(started: PaymentDetails, tag: MonetizationTag) { - let returnValue: PaymentDetails | null = started + let returnValue: PaymentDetails | null = started; if (started.tagType === 'link') { - let error: Error | null = null + let error: Error | null = null; try { - resolvePaymentEndpoint(started.paymentPointer, true) + resolvePaymentEndpoint(started.paymentPointer, true); } catch (e) { - error = e - returnValue = null + error = e; + returnValue = null; } if (!error && returnValue) { - this.linkTagsById.set(started.requestId, tag as HTMLLinkElement) + this.linkTagsById.set(started.requestId, tag as HTMLLinkElement); } else { - this.emit('link-resolve-payment-endpoint-error', tag, error) + this.emit('link-resolve-payment-endpoint-error', tag, error); // const event = new ErrorEvent('error', { error }) - const event = new Event('error') - tag.dispatchEvent(event) + const event = new Event('error'); + tag.dispatchEvent(event); } } - return returnValue + return returnValue; } private clearLinkById(stopped: PaymentDetails) { if (stopped.tagType === 'link') { - this.linkTagsById.delete(stopped.requestId) + this.linkTagsById.delete(stopped.requestId); } } private getPaymentDetails(tag: MonetizationTag): PaymentDetails { - const tagType = getTagType(tag) - const paymentPointer = tag instanceof HTMLMetaElement ? tag.content : tag.href + const tagType = getTagType(tag); + const paymentPointer = tag instanceof HTMLMetaElement ? tag.content : tag.href; return { attrs: getTagAttrs(tag, tagType), requestId: uuidV4(), @@ -425,12 +425,12 @@ export class MonetizationTagManager extends EventEmitter { initiatingUrl: this.window.location.href, tagType: getTagType(tag), fromBody: tag.parentElement != this.document.head, - } + }; } private checkMonetizationAttr(node: HTMLElement) { - debug('checkMonetizationAttr', node) - this.fireOnMonetizationAttrChangedEvent({ node }) + debug('checkMonetizationAttr', node); + this.fireOnMonetizationAttrChangedEvent({ node }); } private onOnMonetizationChangeObserved(records: MutationRecord[]) { @@ -443,7 +443,7 @@ export class MonetizationTagManager extends EventEmitter { this.fireOnMonetizationAttrChangedEvent({ node: record.target, changeDetected: true, - }) + }); } } } @@ -452,34 +452,34 @@ export class MonetizationTagManager extends EventEmitter { node, changeDetected = false, }: FireOnMonetizationChangeIfHaveAttributeParams) { - const attribute = node.getAttribute('onmonetization') + const attribute = node.getAttribute('onmonetization'); if (attribute || changeDetected) { const attributeDetail = { attribute, - } + }; const customEvent = new CustomEvent('onmonetization-attr-changed', { bubbles: true, detail: mozClone(attributeDetail, this.document), - }) - const result = node.dispatchEvent(customEvent) - debug('dispatched onmonetization-attr-changed ev', result) + }); + const result = node.dispatchEvent(customEvent); + debug('dispatched onmonetization-attr-changed ev', result); } } stop() { - this.documentObserver.disconnect() - this.monetizationTagAttrObserver.disconnect() - this.monetizationTags.clear() + this.documentObserver.disconnect(); + this.monetizationTagAttrObserver.disconnect(); + this.monetizationTags.clear(); } atMostOneTagAndNoneInBody() { - let fromBody = false + let fromBody = false; // @ts-ignore for (const value of this.monetizationTags.values()) { if (value.details.fromBody) { - fromBody = true + fromBody = true; } } - return this.monetizationTags.size <= 1 && !fromBody + return this.monetizationTags.size <= 1 && !fromBody; } } diff --git a/src/utils/monetizationTagManager/index.ts b/packages/extension/src/utils/monetizationTagManager/index.ts similarity index 64% rename from src/utils/monetizationTagManager/index.ts rename to packages/extension/src/utils/monetizationTagManager/index.ts index 26b661ba..4581db36 100644 --- a/src/utils/monetizationTagManager/index.ts +++ b/packages/extension/src/utils/monetizationTagManager/index.ts @@ -1,39 +1,39 @@ -import { MonetizationTagManager } from '@/utils/monetizationTagManager/MonetizationTagManager' +import { MonetizationTagManager } from '@/utils/monetizationTagManager/MonetizationTagManager'; interface PaymentDetailsChangeArguments { - started: PaymentDetails | null - stopped: PaymentDetails | null + started: PaymentDetails | null; + stopped: PaymentDetails | null; } interface PaymentDetails { - requestId: string - paymentPointer: string - initiatingUrl: string - fromBody: boolean - tagType: 'link' - attrs: Record + requestId: string; + paymentPointer: string; + initiatingUrl: string; + fromBody: boolean; + tagType: 'link'; + attrs: Record; } export const initMonetizationTagManager = () => { const onPaymentDetailsChange = (details: PaymentDetailsChangeArguments) => { - const { started, stopped } = details + const { started, stopped } = details; if (stopped) { // debug('sending stopped request', JSON.stringify(stopped, null, 2)) // this.stopMonetization(stopped) - console.log('stop monetization') + console.log('stop monetization'); } if (started) { // debug('sending start request', JSON.stringify(started, null, 2)) // void this.startMonetization(started) // console.log('start monetization') } - } + }; const monetizationTagManager = new MonetizationTagManager( window, document, onPaymentDetailsChange, - ) + ); - monetizationTagManager.startWhenDocumentReady() -} + monetizationTagManager.startWhenDocumentReady(); +}; diff --git a/packages/extension/src/utils/monetizationTagManager/mozClone.ts b/packages/extension/src/utils/monetizationTagManager/mozClone.ts new file mode 100644 index 00000000..4909c5d7 --- /dev/null +++ b/packages/extension/src/utils/monetizationTagManager/mozClone.ts @@ -0,0 +1,14 @@ +type DefaultView = WindowProxy & typeof globalThis; +type CloneInto = (obj: unknown, _window: DefaultView | null) => typeof obj; +declare const cloneInto: CloneInto | undefined; + +let cloneIntoRef: CloneInto | undefined; +try { + cloneIntoRef = cloneInto; +} catch (e) { + cloneIntoRef = undefined; +} + +export function mozClone(obj: unknown, document: Document) { + return cloneIntoRef ? cloneIntoRef(obj, document.defaultView) : obj; +} diff --git a/src/utils/monetizationTagManager/resolvePaymentEndpoint.ts b/packages/extension/src/utils/monetizationTagManager/resolvePaymentEndpoint.ts similarity index 78% rename from src/utils/monetizationTagManager/resolvePaymentEndpoint.ts rename to packages/extension/src/utils/monetizationTagManager/resolvePaymentEndpoint.ts index a76af1d0..bf0c046f 100644 --- a/src/utils/monetizationTagManager/resolvePaymentEndpoint.ts +++ b/packages/extension/src/utils/monetizationTagManager/resolvePaymentEndpoint.ts @@ -1,28 +1,30 @@ export class PaymentEndpointError extends Error {} export function resolvePaymentEndpoint(pointerOrUrl: string, urlOnly = false) { - const httpUrl = urlOnly ? pointerOrUrl : pointerOrUrl.replace(/^\$/, 'https://') + const httpUrl = urlOnly ? pointerOrUrl : pointerOrUrl.replace(/^\$/, 'https://'); - let url: URL + let url: URL; try { - url = new URL(httpUrl) + url = new URL(httpUrl); if (!url.protocol.match(/https?:/)) { - const spec = urlOnly ? '' : 'either a payment pointer or ' + const spec = urlOnly ? '' : 'either a payment pointer or '; // noinspection ExceptionCaughtLocallyJS throw new PaymentEndpointError( `SPSP endpoint must be specified as ${spec}fully resolved https:// url, ` + `got ${JSON.stringify(pointerOrUrl)} `, - ) + ); } } catch (e) { if (e instanceof PaymentEndpointError) { - throw e + throw e; } else { - throw new PaymentEndpointError(`Invalid payment pointer/url: ${JSON.stringify(pointerOrUrl)}`) + throw new PaymentEndpointError( + `Invalid payment pointer/url: ${JSON.stringify(pointerOrUrl)}`, + ); } } - const isPaymentPointer = pointerOrUrl.startsWith('$') + const isPaymentPointer = pointerOrUrl.startsWith('$'); if (isPaymentPointer && (url.hash || url.search || url.port || url.username || url.password)) { throw new PaymentEndpointError( @@ -35,7 +37,7 @@ export function resolvePaymentEndpoint(pointerOrUrl: string, urlOnly = false) { username: url.username, password: url.password, }), - ) + ); } - return isPaymentPointer && url.pathname === '/' ? url.href + '.well-known/pay' : url.href + return isPaymentPointer && url.pathname === '/' ? url.href + '.well-known/pay' : url.href; } diff --git a/src/utils/monetizationTagManager/whenDocumentReady.ts b/packages/extension/src/utils/monetizationTagManager/whenDocumentReady.ts similarity index 89% rename from src/utils/monetizationTagManager/whenDocumentReady.ts rename to packages/extension/src/utils/monetizationTagManager/whenDocumentReady.ts index 3a845893..573e2ec9 100644 --- a/src/utils/monetizationTagManager/whenDocumentReady.ts +++ b/packages/extension/src/utils/monetizationTagManager/whenDocumentReady.ts @@ -1,15 +1,15 @@ export function whenDocumentReady(document: Document, start: () => void) { if (document.readyState === 'interactive' || document.readyState === 'complete') { - start() + start(); } else { document.addEventListener( 'readystatechange', () => { if (document.readyState === 'interactive') { - start() + start(); } }, { once: true }, - ) + ); } } diff --git a/src/utils/polyfill.ts b/packages/extension/src/utils/polyfill.ts similarity index 99% rename from src/utils/polyfill.ts rename to packages/extension/src/utils/polyfill.ts index 6acf624e..ef5fd5eb 100644 --- a/src/utils/polyfill.ts +++ b/packages/extension/src/utils/polyfill.ts @@ -99,4 +99,4 @@ export const wm2Polyfill = ` } }, { capture: true }) dbg('add coil-onmonetization-v2-attr-changed handler end') -` +`; diff --git a/src/utils/sendMessages.ts b/packages/extension/src/utils/sendMessages.ts similarity index 72% rename from src/utils/sendMessages.ts rename to packages/extension/src/utils/sendMessages.ts index 9e4c6dc5..a6f767c1 100644 --- a/src/utils/sendMessages.ts +++ b/packages/extension/src/utils/sendMessages.ts @@ -1,4 +1,4 @@ -import { Runtime, runtime, Tabs, tabs } from 'webextension-polyfill' +import { Runtime, runtime, Tabs, tabs } from 'webextension-polyfill'; /** * Send Message to Background Script @@ -10,8 +10,8 @@ export const sendMessage = ( msg: EXTMessage, options?: Runtime.SendMessageOptionsType, ): Promise => { - return runtime.sendMessage(msg, options) -} + return runtime.sendMessage(msg, options); +}; /** * Send Message to Content Script @@ -21,8 +21,8 @@ export const sendMessageToTab = ( msg: EXTMessage, options?: Tabs.SendMessageOptionsType, ): Promise => { - return tabs.sendMessage(tab.id as number, msg, options) -} + return tabs.sendMessage(tab.id as number, msg, options); +}; /** * Send Message to Content Script @@ -31,13 +31,13 @@ export const sendMessageToActiveTab = async ( msg: EXTMessage, options?: Tabs.SendMessageOptionsType, ): Promise => { - let activeTab: Tabs.Tab + let activeTab: Tabs.Tab; try { - const activeTabs = await tabs.query({ active: true, currentWindow: true }) - activeTab = activeTabs[0] + const activeTabs = await tabs.query({ active: true, currentWindow: true }); + activeTab = activeTabs[0]; } catch (error) { - console.log('[===== Error in sendMessageToActiveTab =====]', error) - throw `Error in sendMessageToActiveTab` + console.log('[===== Error in sendMessageToActiveTab =====]', error); + throw `Error in sendMessageToActiveTab`; } - return sendMessageToTab(activeTab, msg, options) -} + return sendMessageToTab(activeTab, msg, options); +}; diff --git a/packages/extension/src/utils/storage.ts b/packages/extension/src/utils/storage.ts new file mode 100755 index 00000000..7d08ce70 --- /dev/null +++ b/packages/extension/src/utils/storage.ts @@ -0,0 +1,3 @@ +import { storage } from 'webextension-polyfill'; + +export default storage.sync ? storage.sync : storage.local; diff --git a/tailwind.config.ts b/packages/extension/tailwind.config.ts similarity index 97% rename from tailwind.config.ts rename to packages/extension/tailwind.config.ts index f44ecc34..cd8be1ab 100644 --- a/tailwind.config.ts +++ b/packages/extension/tailwind.config.ts @@ -1,4 +1,4 @@ -import { Config } from 'tailwindcss' +import { Config } from 'tailwindcss'; module.exports = { content: ['./src/**/*.{html,js,jsx,ts,tsx}', './src/components/**/*.{js,jsx,ts,tsx}'], @@ -42,4 +42,4 @@ module.exports = { }, }, plugins: [require('@tailwindcss/forms')], -} satisfies Config +} satisfies Config; diff --git a/packages/extension/tsconfig.json b/packages/extension/tsconfig.json new file mode 100644 index 00000000..42472d5e --- /dev/null +++ b/packages/extension/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "esModuleInterop": true, + "module": "commonjs", + "target": "es5", + "allowJs": true, + "jsx": "react", + "sourceMap": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "allowUmdGlobalAccess": true, + "resolveJsonModule": true, + "paths": { + "@/utils/*": ["./src/utils/*"], + "@/popup/*": ["./src/popup/*"], + "@/background/*": ["./src/background/*"], + "@/content/*": ["./src/content/*"], + "@/assets/*": ["./src/assets/*"], + "@/components/*": ["./src/components/*"], + "@/types/*": ["./src/types/*"], + "@/hooks/*": ["./src/hooks/*"] + } + }, + "include": ["./src/**/*", "./jest.config.ts", "./jest.setup.ts"], + "exclude": ["dist", "dev", "temp"] +} diff --git a/webpack.config.ts b/packages/extension/webpack.config.ts similarity index 96% rename from webpack.config.ts rename to packages/extension/webpack.config.ts index 2222ff5f..efa7b11a 100644 --- a/webpack.config.ts +++ b/packages/extension/webpack.config.ts @@ -1,5 +1,5 @@ -import path from 'path' -import TerserPlugin from 'terser-webpack-plugin' +import path from 'path'; +import TerserPlugin from 'terser-webpack-plugin'; import { config, @@ -17,7 +17,7 @@ import { getProgressPlugins, getResolves, getZipPlugins, -} from './webpack.config.utils' +} from './webpack.config.utils'; let generalConfig: any = { mode: @@ -74,7 +74,7 @@ let generalConfig: any = { resolve: getResolves(), entry: getEntry(Directories.SRC_DIR), output: getOutput(config.TARGET, config.OUTPUT_DIR), -} +}; let plugins: any[] = [ ...getCleanWebpackPlugins( @@ -86,7 +86,7 @@ let plugins: any[] = [ ...getExtensionManifestPlugins(), ...getHTMLPlugins(config.TARGET, config.OUTPUT_DIR, Directories.SRC_DIR), ...getCopyPlugins(config.TARGET, config.OUTPUT_DIR, Directories.SRC_DIR), -] +]; if (config.NODE_ENV === 'development') { generalConfig = { @@ -103,7 +103,7 @@ if (config.NODE_ENV === 'development') { aggregateTimeout: 200, poll: 1000, }, - } + }; plugins = [ ...plugins, @@ -112,7 +112,7 @@ if (config.NODE_ENV === 'development') { WM_WALLET_ADDRESS: 'https://ilp.rafiki.money/wm-dev', }), ...getExtensionReloaderPlugins(), - ] + ]; } if (config.NODE_ENV === 'profile') { @@ -125,7 +125,7 @@ if (config.NODE_ENV === 'profile') { errors: true, hash: true, }, - } + }; plugins = [ ...plugins, @@ -134,7 +134,7 @@ if (config.NODE_ENV === 'profile') { WM_WALLET_ADDRESS: 'https://ilp.rafiki.money/wm-dev', }), ...getAnalyzerPlugins(), - ] + ]; } if (config.NODE_ENV === 'production') { @@ -154,7 +154,7 @@ if (config.NODE_ENV === 'production') { }), ], }, - } + }; plugins = [ ...plugins, @@ -164,7 +164,7 @@ if (config.NODE_ENV === 'production') { }), ...getZipPlugins(config.TARGET, Directories.DIST_DIR), - ] + ]; } export default [ @@ -172,4 +172,4 @@ export default [ ...generalConfig, plugins, }, -] +]; diff --git a/webpack.config.utils.ts b/packages/extension/webpack.config.utils.ts similarity index 84% rename from webpack.config.utils.ts rename to packages/extension/webpack.config.utils.ts index 176e4579..0acc146c 100644 --- a/webpack.config.utils.ts +++ b/packages/extension/webpack.config.utils.ts @@ -1,33 +1,32 @@ -import { CleanWebpackPlugin } from 'clean-webpack-plugin' -import CopyWebpackPlugin from 'copy-webpack-plugin' -import ESLintPlugin from 'eslint-webpack-plugin' -import HtmlWebpackPlugin from 'html-webpack-plugin' -import path from 'path' -import { DefinePlugin, ProgressPlugin } from 'webpack' -import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer' -import WebpackExtensionManifestPlugin from 'webpack-extension-manifest-plugin' -import ZipPlugin from 'zip-webpack-plugin' +import { CleanWebpackPlugin } from 'clean-webpack-plugin'; +import CopyWebpackPlugin from 'copy-webpack-plugin'; +import ESLintPlugin from 'eslint-webpack-plugin'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import path from 'path'; +import { DefinePlugin, ProgressPlugin } from 'webpack'; +import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; +import WebpackExtensionManifestPlugin from 'webpack-extension-manifest-plugin'; +import ZipPlugin from 'zip-webpack-plugin'; -const ExtReloader = require('webpack-ext-reloader-mv3') +import manifestChrome from './src/manifest/chrome.json'; +import manifestEdge from './src/manifest/edge.json'; +import manifestFirefox from './src/manifest/firefox.json'; +import manifestOpera from './src/manifest/opera.json'; -const manifestChrome = require('./src/manifest/chrome.json') -const manifestFirefox = require('./src/manifest/firefox.json') -const manifestOpera = require('./src/manifest/opera.json') -const manifestEdge = require('./src/manifest/edge.json') +// eslint-disable-next-line @typescript-eslint/no-var-requires +const ExtReloader = require('webpack-ext-reloader-mv3'); const manifest = { chrome: manifestChrome, firefox: manifestFirefox, opera: manifestOpera, edge: manifestEdge, -} - -const dotenv = require('dotenv').config({ path: __dirname + '/.env' }) +}; interface EnvironmentConfig { - NODE_ENV: string - OUTPUT_DIR: string - TARGET: string + NODE_ENV: string; + OUTPUT_DIR: string; + TARGET: string; } export const Directories = { @@ -35,7 +34,7 @@ export const Directories = { DIST_DIR: 'dist', TEMP_DIR: 'temp', SRC_DIR: 'src', -} +}; /** * Environment Config @@ -50,7 +49,7 @@ const EnvConfig: EnvironmentConfig = { : Directories.DEV_DIR, ...(process.env.NODE_ENV ? { NODE_ENV: process.env.NODE_ENV } : { NODE_ENV: 'development' }), ...(process.env.TARGET ? { TARGET: process.env.TARGET } : { TARGET: 'chrome' }), -} +}; /** * Get HTML Plugins @@ -71,7 +70,7 @@ export const getHTMLPlugins = ( template: path.resolve(__dirname, `${sourceDir}/popup/index.html`), chunks: ['popup'], }), -] +]; /** * Get DefinePlugins @@ -84,7 +83,7 @@ export const getDefinePlugins = (config: { SIGNATURES_URL: string; WM_WALLET_ADD CONFIG_SIGNATURES_URL: JSON.stringify(config.SIGNATURES_URL), CONFIG_WM_WALLET_ADDRESS: JSON.stringify(config.WM_WALLET_ADDRESS), }), -] +]; /** * Get Output Configurations @@ -97,8 +96,8 @@ export const getOutput = (browserDir: string, outputDir = Directories.DEV_DIR) = return { path: path.resolve(process.cwd(), `${outputDir}/${browserDir}`), filename: '[name]/[name].js', - } -} + }; +}; /** * Get Entry Points @@ -111,8 +110,8 @@ export const getEntry = (sourceDir = Directories.SRC_DIR) => { popup: [path.resolve(__dirname, `${sourceDir}/popup/index.tsx`)], content: [path.resolve(__dirname, `${sourceDir}/content/index.tsx`)], background: [path.resolve(__dirname, `${sourceDir}/background/index.ts`)], - } -} + }; +}; /** * Get CopyPlugins @@ -140,8 +139,8 @@ export const getCopyPlugins = ( }, ], }), - ] -} + ]; +}; /** * Get ZipPlugins @@ -166,8 +165,8 @@ export const getZipPlugins = (browserDir: string, outputDir = Directories.DIST_D forceZip64Format: false, }, }), - ] -} + ]; +}; /** * Get Analyzer Plugins @@ -179,8 +178,8 @@ export const getAnalyzerPlugins = () => { new BundleAnalyzerPlugin({ analyzerMode: 'server', }), - ] -} + ]; +}; /** * Get CleanWebpackPlugins @@ -197,8 +196,8 @@ export const getCleanWebpackPlugins = (...dirs: string[]) => { cleanStaleWebpackAssets: false, verbose: true, }), - ] -} + ]; +}; /** * Get Resolves @@ -221,8 +220,8 @@ export const getResolves = () => { '@/hooks': path.resolve(__dirname, './src/hooks/'), }, extensions: ['.js', '.jsx', '.ts', '.tsx'], - } -} + }; +}; /** * Get Extension Manifest Plugins @@ -234,12 +233,12 @@ export const getExtensionManifestPlugins = () => { new WebpackExtensionManifestPlugin({ config: { base: (manifest as any)[EnvConfig.TARGET] }, }), - ] -} + ]; +}; export const eslintOptions = { fix: true, -} +}; /** * Get Eslint Plugins @@ -247,8 +246,8 @@ export const eslintOptions = { * @returns */ export const getEslintPlugins = (options = eslintOptions) => { - return [new ESLintPlugin(options)] -} + return [new ESLintPlugin(options)]; +}; /** * Get Progress Plugins @@ -256,14 +255,14 @@ export const getEslintPlugins = (options = eslintOptions) => { * @returns */ export const getProgressPlugins = () => { - return [new ProgressPlugin()] -} + return [new ProgressPlugin()]; +}; /** * Environment Configuration Variables * */ -export const config = EnvConfig +export const config = EnvConfig; /** * Get Extension Reloader Plugin @@ -281,5 +280,5 @@ export const getExtensionReloaderPlugins = () => { extensionPage: ['popup', 'options'], }, }), - ] -} + ]; +}; diff --git a/packages/server/.eslintrc.js b/packages/server/.eslintrc.js new file mode 100644 index 00000000..1a4431d8 --- /dev/null +++ b/packages/server/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + node: true, + }, +}; diff --git a/packages/server/package.json b/packages/server/package.json new file mode 100644 index 00000000..9f2797f6 --- /dev/null +++ b/packages/server/package.json @@ -0,0 +1,29 @@ +{ + "name": "@interledger/wm-server", + "homepage": "https://github.com/interledger/web-monetization-extension/tree/main/packages/server", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:interledeger/web-monetization-extension.git", + "directory": "packages/server" + }, + "license": "Apache-2.0", + "author": "Interledger Tech Team ", + "scripts": { + "build": "tsc", + "dev": "tsx --watch ./src/index.ts", + "lint": "eslint . --ext ts --cache --cache-location 'node_modules/.cache/.eslintcache' --max-warnings 0", + "lint:fix": "eslint . --ext ts --cache --cache-location 'node_modules/.cache/.eslintcache' --fix", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@interledger/http-signature-utils": "^2.0.0", + "koa": "^2.14.2", + "koa-bodyparser": "^4.4.1" + }, + "devDependencies": { + "@types/koa": "^2.14.0", + "@types/koa-bodyparser": "^4.3.12", + "tsx": "^4.7.0" + } +} diff --git a/local-signatures/index.ts b/packages/server/src/index.ts similarity index 56% rename from local-signatures/index.ts rename to packages/server/src/index.ts index f382a684..7761e50e 100644 --- a/local-signatures/index.ts +++ b/packages/server/src/index.ts @@ -3,9 +3,9 @@ import { Headers, loadBase64Key, RequestLike, -} from '@interledger/http-signature-utils' -import Koa from 'koa' -import bodyParser from 'koa-bodyparser' +} from '@interledger/http-signature-utils'; +import Koa from 'koa'; +import bodyParser from 'koa-bodyparser'; interface Context extends Koa.ParameterizedContext {} @@ -13,69 +13,69 @@ interface Context interface GenerateSignatureRequestBody extends RequestLike {} function validateBody(body: any): body is GenerateSignatureRequestBody { - return !!body.headers && !!body.method && !!body.url + return !!body.headers && !!body.method && !!body.url; } async function validatePath(ctx: Context, next: Koa.Next): Promise { if (ctx.path !== '/') { - ctx.status = 404 + ctx.status = 403; } else { - await next() + await next(); } } async function validateMethod(ctx: Context, next: Koa.Next): Promise { if (ctx.method !== 'POST') { - ctx.status = 405 + ctx.status = 405; } else { - await next() + await next(); } } async function createHeadersHandler(ctx: Context): Promise { - const { body } = ctx.request + const { body } = ctx.request; if (!validateBody(body)) { - ctx.throw('Invalid request body', 400) + ctx.throw('Invalid request body', 400); } - let privateKey: ReturnType + let privateKey: ReturnType; try { - privateKey = loadBase64Key(BASE64_PRIVATE_KEY) + privateKey = loadBase64Key(BASE64_PRIVATE_KEY); } catch { - ctx.throw('Not a valid private key', 400) + ctx.throw('Not a valid private key', 400); } if (privateKey === undefined) { - ctx.throw('Not an Ed25519 private key', 400) + ctx.throw('Not an Ed25519 private key', 400); } const headers = await createHeaders({ request: body, privateKey, keyId: KEY_ID, - }) + }); - delete headers['Content-Length'] - delete headers['Content-Type'] + delete headers['Content-Length']; + delete headers['Content-Type']; - ctx.body = headers + ctx.body = headers; } -const PORT = 3000 +const PORT = 3000; const BASE64_PRIVATE_KEY = - 'LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSUUvVlJTRVUzYS9CTUE2cmhUQnZmKzcxMG10YWlmbkF6SzFsWGpDK0QrSTkKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQ==' -const KEY_ID = 'f0ac2190-54d5-47c8-b061-221e7068d823' + 'LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSUUvVlJTRVUzYS9CTUE2cmhUQnZmKzcxMG10YWlmbkF6SzFsWGpDK0QrSTkKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQ=='; +const KEY_ID = 'f0ac2190-54d5-47c8-b061-221e7068d823'; -const app = new Koa() +const app = new Koa(); -app.use(bodyParser()) -app.use(validatePath) -app.use(validateMethod) -app.use(createHeadersHandler) +app.use(bodyParser()); +app.use(validatePath); +app.use(validateMethod); +app.use(createHeadersHandler); app.listen(3000, () => { // eslint-disable-next-line no-console - console.log(`Local signatures server started on port ${PORT}`) -}) + console.log(`Local signatures server started on port ${PORT}`); +}); diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json new file mode 100644 index 00000000..b5a366a6 --- /dev/null +++ b/packages/server/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020"], + "module": "commonjs", + "moduleResolution": "Node", + "resolveJsonModule": true, + "allowJs": false, + "rootDir": "./src", + "declarationMap": true, + "sourceMap": true, + "declaration": true, + "inlineSourceMap": false, + "outDir": "./dist", + "removeComments": true, + "isolatedModules": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 043d41b6..d4f8c490 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,249 +4,249 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - axios: - specifier: ^1.5.1 - version: 1.6.2 - class-variance-authority: - specifier: ^0.7.0 - version: 0.7.0 - clean-webpack-plugin: - specifier: ^4.0.0 - version: 4.0.0(webpack@5.89.0) - copy-webpack-plugin: - specifier: ^11.0.0 - version: 11.0.0(webpack@5.89.0) - core-js: - specifier: ^3.32.1 - version: 3.34.0 - css-loader: - specifier: ^6.8.1 - version: 6.8.1(webpack@5.89.0) - dotenv: - specifier: ^16.3.1 - version: 16.3.1 - events: - specifier: ^3.3.0 - version: 3.3.0 - html-webpack-plugin: - specifier: ^5.5.3 - version: 5.5.4(webpack@5.89.0) - prop-types: - specifier: ^15.8.1 - version: 15.8.1 - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - sass: - specifier: ^1.66.1 - version: 1.69.5 - sass-loader: - specifier: ^13.3.2 - version: 13.3.2(sass@1.69.5)(webpack@5.89.0) - style-loader: - specifier: ^3.3.3 - version: 3.3.3(webpack@5.89.0) - tailwind-merge: - specifier: ^2.1.0 - version: 2.1.0 - terser-webpack-plugin: - specifier: ^5.3.9 - version: 5.3.9(webpack@5.89.0) - uuid: - specifier: ^9.0.1 - version: 9.0.1 - webextension-polyfill: - specifier: ^0.10.0 - version: 0.10.0 - webpack: - specifier: ^5.88.2 - version: 5.89.0(webpack-cli@5.1.4) - webpack-bundle-analyzer: - specifier: ^4.9.1 - version: 4.10.1 - webpack-ext-reloader-mv3: - specifier: ^2.1.1 - version: 2.1.1(webpack-cli@5.1.4)(webpack@5.89.0) - webpack-extension-manifest-plugin: - specifier: ^0.8.0 - version: 0.8.0 - zip-webpack-plugin: - specifier: ^4.0.1 - version: 4.0.1(webpack-sources@3.2.3)(webpack@5.89.0) - -devDependencies: - '@interledger/http-signature-utils': - specifier: ^2.0.0 - version: 2.0.0 - '@tailwindcss/forms': - specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.4.0) - '@testing-library/jest-dom': - specifier: ^6.1.3 - version: 6.1.5(@types/jest@29.5.11)(jest@29.7.0) - '@testing-library/react': - specifier: ^14.0.0 - version: 14.1.2(react-dom@18.2.0)(react@18.2.0) - '@types/chrome': - specifier: ^0.0.244 - version: 0.0.244 - '@types/inboxsdk': - specifier: ^2.0.11 - version: 2.0.14 - '@types/jest': - specifier: ^29.5.5 - version: 29.5.11 - '@types/jquery': - specifier: ^3.5.18 - version: 3.5.29 - '@types/koa': - specifier: ^2.13.12 - version: 2.13.12 - '@types/koa-bodyparser': - specifier: ^4.3.12 - version: 4.3.12 - '@types/lodash': - specifier: ^4.14.197 - version: 4.14.202 - '@types/node': - specifier: ^20.8.4 - version: 20.10.4 - '@types/react': - specifier: ^18.2.21 - version: 18.2.42 - '@types/react-dom': - specifier: ^18.2.7 - version: 18.2.17 - '@types/react-router-dom': - specifier: ^5.3.3 - version: 5.3.3 - '@types/redux': - specifier: ^3.6.31 - version: 3.6.31 - '@types/uuid': - specifier: ^9.0.5 - version: 9.0.7 - '@types/webextension-polyfill': - specifier: ^0.10.7 - version: 0.10.7 - '@types/webpack-bundle-analyzer': - specifier: ^4.6.0 - version: 4.6.3(webpack-cli@5.1.4) - '@types/zip-webpack-plugin': - specifier: ^3.0.4 - version: 3.0.6 - '@typescript-eslint/eslint-plugin': - specifier: ^6.5.0 - version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3) - '@typescript-eslint/parser': - specifier: ^6.5.0 - version: 6.13.2(eslint@8.55.0)(typescript@5.3.3) - autoprefixer: - specifier: ^10.4.16 - version: 10.4.16(postcss@8.4.32) - concurrently: - specifier: ^8.2.2 - version: 8.2.2 - eslint: - specifier: ^8.48.0 - version: 8.55.0 - eslint-config-airbnb: - specifier: ^19.0.4 - version: 19.0.4(eslint-plugin-import@2.29.0)(eslint-plugin-jsx-a11y@6.8.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.33.2)(eslint@8.55.0) - eslint-config-prettier: - specifier: ^9.0.0 - version: 9.1.0(eslint@8.55.0) - eslint-plugin-babel: - specifier: ^5.3.1 - version: 5.3.1(eslint@8.55.0) - eslint-plugin-cypress: - specifier: ^2.12.1 - version: 2.15.1(eslint@8.55.0) - eslint-plugin-html: - specifier: ^7.1.0 - version: 7.1.0 - eslint-plugin-import: - specifier: ^2.28.0 - version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0) - eslint-plugin-jest: - specifier: ^26.1.5 - version: 26.9.0(@typescript-eslint/eslint-plugin@6.13.2)(eslint@8.55.0)(jest@29.7.0)(typescript@5.3.3) - eslint-plugin-jsx-a11y: - specifier: ^6.7.1 - version: 6.8.0(eslint@8.55.0) - eslint-plugin-node: - specifier: ^11.1.0 - version: 11.1.0(eslint@8.55.0) - eslint-plugin-prettier: - specifier: ^5.0.0 - version: 5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.0) - eslint-plugin-react: - specifier: ^7.33.2 - version: 7.33.2(eslint@8.55.0) - eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.0(eslint@8.55.0) - eslint-plugin-simple-import-sort: - specifier: ^7.0.0 - version: 7.0.0(eslint@8.55.0) - eslint-webpack-plugin: - specifier: ^4.0.1 - version: 4.0.1(eslint@8.55.0)(webpack@5.89.0) - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) - jest-chrome: - specifier: ^0.8.0 - version: 0.8.0(jest@29.7.0) - jest-environment-jsdom: - specifier: ^29.7.0 - version: 29.7.0 - jest-transform-stub: - specifier: ^2.0.0 - version: 2.0.0 - koa: - specifier: ^2.14.2 - version: 2.14.2 - koa-bodyparser: - specifier: ^4.4.1 - version: 4.4.1 - postcss: - specifier: ^8.4.32 - version: 8.4.32 - postcss-loader: - specifier: ^7.3.3 - version: 7.3.3(postcss@8.4.32)(typescript@5.3.3)(webpack@5.89.0) - prettier: - specifier: ^3.0.3 - version: 3.1.0 - react-router-dom: - specifier: ^6.21.1 - version: 6.21.1(react-dom@18.2.0)(react@18.2.0) - tailwindcss: - specifier: ^3.4.0 - version: 3.4.0(ts-node@10.9.2) - ts-jest: - specifier: ^29.1.1 - version: 29.1.1(@babel/core@7.23.5)(jest@29.7.0)(typescript@5.3.3) - ts-loader: - specifier: ^9.4.4 - version: 9.5.1(typescript@5.3.3)(webpack@5.89.0) - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@20.10.4)(typescript@5.3.3) - tsx: - specifier: ^4.6.2 - version: 4.6.2 - typescript: - specifier: ^5.2.2 - version: 5.3.3 - webpack-cli: - specifier: ^5.1.4 - version: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0) +importers: + + .: + devDependencies: + '@testing-library/jest-dom': + specifier: ^6.1.3 + version: 6.1.5(@types/jest@29.5.11)(jest@29.7.0) + '@testing-library/react': + specifier: ^14.0.0 + version: 14.1.2(react-dom@18.2.0)(react@18.2.0) + '@types/jest': + specifier: ^29.5.5 + version: 29.5.11 + '@types/node': + specifier: ^18.19.9 + version: 18.19.9 + '@typescript-eslint/eslint-plugin': + specifier: ^6.5.0 + version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: ^6.5.0 + version: 6.13.2(eslint@8.55.0)(typescript@5.3.3) + concurrently: + specifier: ^8.2.2 + version: 8.2.2 + eslint: + specifier: ^8.48.0 + version: 8.55.0 + eslint-config-airbnb: + specifier: ^19.0.4 + version: 19.0.4(eslint-plugin-import@2.29.0)(eslint-plugin-jsx-a11y@6.8.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.33.2)(eslint@8.55.0) + eslint-plugin-babel: + specifier: ^5.3.1 + version: 5.3.1(eslint@8.55.0) + eslint-plugin-html: + specifier: ^7.1.0 + version: 7.1.0 + eslint-plugin-import: + specifier: ^2.28.0 + version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0) + eslint-plugin-jest: + specifier: ^27.6.3 + version: 27.6.3(@typescript-eslint/eslint-plugin@6.13.2)(eslint@8.55.0)(jest@29.7.0)(typescript@5.3.3) + eslint-plugin-jsx-a11y: + specifier: ^6.7.1 + version: 6.8.0(eslint@8.55.0) + eslint-plugin-node: + specifier: ^11.1.0 + version: 11.1.0(eslint@8.55.0) + eslint-plugin-react: + specifier: ^7.33.2 + version: 7.33.2(eslint@8.55.0) + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.55.0) + eslint-plugin-simple-import-sort: + specifier: ^7.0.0 + version: 7.0.0(eslint@8.55.0) + eslint-webpack-plugin: + specifier: ^4.0.1 + version: 4.0.1(eslint@8.55.0)(webpack@5.89.0) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) + jest-chrome: + specifier: ^0.8.0 + version: 0.8.0(jest@29.7.0) + jest-environment-jsdom: + specifier: ^29.7.0 + version: 29.7.0 + jest-transform-stub: + specifier: ^2.0.0 + version: 2.0.0 + prettier: + specifier: ^3.0.3 + version: 3.1.0 + prettier-plugin-tailwindcss: + specifier: ^0.5.11 + version: 0.5.11(prettier@3.1.0) + tailwindcss: + specifier: ^3.4.0 + version: 3.4.0(ts-node@10.9.2) + ts-jest: + specifier: ^29.1.1 + version: 29.1.1(@babel/core@7.23.5)(jest@29.7.0)(typescript@5.3.3) + ts-loader: + specifier: ^9.4.4 + version: 9.5.1(typescript@5.3.3)(webpack@5.89.0) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@18.19.9)(typescript@5.3.3) + typescript: + specifier: ^5.2.2 + version: 5.3.3 + + packages/extension: + dependencies: + axios: + specifier: ^1.5.1 + version: 1.6.2 + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 + clean-webpack-plugin: + specifier: ^4.0.0 + version: 4.0.0(webpack@5.89.0) + copy-webpack-plugin: + specifier: ^11.0.0 + version: 11.0.0(webpack@5.89.0) + css-loader: + specifier: ^6.8.1 + version: 6.8.1(webpack@5.89.0) + events: + specifier: ^3.3.0 + version: 3.3.0 + html-webpack-plugin: + specifier: ^5.5.3 + version: 5.5.4(webpack@5.89.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: ^6.21.1 + version: 6.21.1(react-dom@18.2.0)(react@18.2.0) + sass: + specifier: ^1.66.1 + version: 1.69.5 + sass-loader: + specifier: ^13.3.2 + version: 13.3.2(sass@1.69.5)(webpack@5.89.0) + style-loader: + specifier: ^3.3.3 + version: 3.3.3(webpack@5.89.0) + tailwind-merge: + specifier: ^2.1.0 + version: 2.1.0 + terser-webpack-plugin: + specifier: ^5.3.9 + version: 5.3.9(webpack@5.89.0) + uuid: + specifier: ^9.0.1 + version: 9.0.1 + webextension-polyfill: + specifier: ^0.10.0 + version: 0.10.0 + webpack: + specifier: ^5.88.2 + version: 5.89.0(webpack-cli@5.1.4) + webpack-bundle-analyzer: + specifier: ^4.9.1 + version: 4.10.1 + webpack-ext-reloader-mv3: + specifier: ^2.1.1 + version: 2.1.1(webpack-cli@5.1.4)(webpack@5.89.0) + webpack-extension-manifest-plugin: + specifier: ^0.8.0 + version: 0.8.0 + zip-webpack-plugin: + specifier: ^4.0.1 + version: 4.0.1(webpack-sources@3.2.3)(webpack@5.89.0) + devDependencies: + '@tailwindcss/forms': + specifier: ^0.5.7 + version: 0.5.7(tailwindcss@3.4.0) + '@types/chrome': + specifier: ^0.0.244 + version: 0.0.244 + '@types/inboxsdk': + specifier: ^2.0.11 + version: 2.0.14 + '@types/jest': + specifier: ^29.5.5 + version: 29.5.11 + '@types/node': + specifier: ^20.8.4 + version: 20.10.4 + '@types/react': + specifier: ^18.2.21 + version: 18.2.42 + '@types/react-dom': + specifier: ^18.2.7 + version: 18.2.17 + '@types/react-router-dom': + specifier: ^5.3.3 + version: 5.3.3 + '@types/uuid': + specifier: ^9.0.5 + version: 9.0.7 + '@types/webextension-polyfill': + specifier: ^0.10.7 + version: 0.10.7 + '@types/webpack-bundle-analyzer': + specifier: ^4.6.0 + version: 4.6.3(webpack-cli@5.1.4) + '@types/zip-webpack-plugin': + specifier: ^3.0.4 + version: 3.0.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.5.0 + version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: ^6.5.0 + version: 6.13.2(eslint@8.55.0)(typescript@5.3.3) + autoprefixer: + specifier: ^10.4.16 + version: 10.4.16(postcss@8.4.32) + postcss: + specifier: ^8.4.32 + version: 8.4.32 + postcss-loader: + specifier: ^7.3.3 + version: 7.3.3(postcss@8.4.32)(typescript@5.3.3)(webpack@5.89.0) + tailwindcss: + specifier: ^3.4.0 + version: 3.4.0(ts-node@10.9.2) + webpack-cli: + specifier: ^5.1.4 + version: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0) + + packages/server: + dependencies: + '@interledger/http-signature-utils': + specifier: ^2.0.0 + version: 2.0.0 + koa: + specifier: ^2.14.2 + version: 2.14.2 + koa-bodyparser: + specifier: ^4.4.1 + version: 4.4.1 + devDependencies: + '@types/koa': + specifier: ^2.14.0 + version: 2.14.0 + '@types/koa-bodyparser': + specifier: ^4.3.12 + version: 4.3.12 + tsx: + specifier: ^4.7.0 + version: 4.7.0 packages: @@ -618,8 +618,17 @@ packages: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} - /@esbuild/android-arm64@0.18.20: - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + /@esbuild/aix-ppc64@0.19.12: + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.19.12: + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -627,8 +636,8 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.18.20: - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + /@esbuild/android-arm@0.19.12: + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -636,8 +645,8 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.18.20: - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + /@esbuild/android-x64@0.19.12: + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -645,8 +654,8 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.18.20: - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + /@esbuild/darwin-arm64@0.19.12: + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -654,8 +663,8 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.18.20: - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + /@esbuild/darwin-x64@0.19.12: + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -663,8 +672,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.18.20: - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + /@esbuild/freebsd-arm64@0.19.12: + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -672,8 +681,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.18.20: - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + /@esbuild/freebsd-x64@0.19.12: + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -681,8 +690,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.18.20: - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + /@esbuild/linux-arm64@0.19.12: + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -690,8 +699,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.18.20: - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + /@esbuild/linux-arm@0.19.12: + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -699,8 +708,8 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.18.20: - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + /@esbuild/linux-ia32@0.19.12: + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -708,8 +717,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.18.20: - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + /@esbuild/linux-loong64@0.19.12: + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -717,8 +726,8 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.18.20: - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + /@esbuild/linux-mips64el@0.19.12: + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -726,8 +735,8 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.18.20: - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + /@esbuild/linux-ppc64@0.19.12: + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -735,8 +744,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.18.20: - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + /@esbuild/linux-riscv64@0.19.12: + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -744,8 +753,8 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.18.20: - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + /@esbuild/linux-s390x@0.19.12: + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -753,8 +762,8 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.18.20: - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + /@esbuild/linux-x64@0.19.12: + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -762,8 +771,8 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.18.20: - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + /@esbuild/netbsd-x64@0.19.12: + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -771,8 +780,8 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.18.20: - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + /@esbuild/openbsd-x64@0.19.12: + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -780,8 +789,8 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.18.20: - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + /@esbuild/sunos-x64@0.19.12: + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -789,8 +798,8 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.18.20: - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + /@esbuild/win32-arm64@0.19.12: + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -798,8 +807,8 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.18.20: - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + /@esbuild/win32-ia32@0.19.12: + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -807,8 +816,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.18.20: - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + /@esbuild/win32-x64@0.19.12: + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -880,7 +889,7 @@ packages: httpbis-digest-headers: 1.0.0 jose: 4.15.4 uuid: 9.0.1 - dev: true + dev: false /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} @@ -1168,25 +1177,13 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - /@pkgr/utils@2.4.2: - resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dependencies: - cross-spawn: 7.0.3 - fast-glob: 3.3.2 - is-glob: 4.0.3 - open: 9.1.0 - picocolors: 1.0.0 - tslib: 2.6.2 - dev: true - /@polka/url@1.0.0-next.24: resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} /@remix-run/router@1.14.1: resolution: {integrity: sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==} engines: {node: '>=14.0.0'} - dev: true + dev: false /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -1252,7 +1249,7 @@ packages: chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.5.16 - jest: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + jest: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) lodash: 4.17.21 redent: 3.0.0 dev: true @@ -1474,12 +1471,6 @@ packages: pretty-format: 29.7.0 dev: true - /@types/jquery@3.5.29: - resolution: {integrity: sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==} - dependencies: - '@types/sizzle': 2.3.8 - dev: true - /@types/jsdom@20.0.1: resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} dependencies: @@ -1502,17 +1493,17 @@ packages: /@types/koa-bodyparser@4.3.12: resolution: {integrity: sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==} dependencies: - '@types/koa': 2.13.12 + '@types/koa': 2.14.0 dev: true /@types/koa-compose@3.2.8: resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} dependencies: - '@types/koa': 2.13.12 + '@types/koa': 2.14.0 dev: true - /@types/koa@2.13.12: - resolution: {integrity: sha512-vAo1KuDSYWFDB4Cs80CHvfmzSQWeUb909aQib0C0aFx4sw0K9UZFz2m5jaEP+b3X1+yr904iQiruS0hXi31jbw==} + /@types/koa@2.14.0: + resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==} dependencies: '@types/accepts': 1.3.7 '@types/content-disposition': 0.5.8 @@ -1524,10 +1515,6 @@ packages: '@types/node': 20.10.4 dev: true - /@types/lodash@4.14.202: - resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} - dev: true - /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true @@ -1540,6 +1527,12 @@ packages: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} dev: false + /@types/node@18.19.9: + resolution: {integrity: sha512-oZFKlC8l5YtzGQNT4zC2PiSSKzQVZ8bAwwd+EYdPLtyk0nSEq6O16SkK+rkkT2eflDAbormJgEF3QnH3oDrTSw==} + dependencies: + undici-types: 5.26.5 + dev: true + /@types/node@20.10.4: resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==} dependencies: @@ -1586,10 +1579,6 @@ packages: csstype: 3.1.2 dev: true - /@types/redux@3.6.31: - resolution: {integrity: sha512-UEa68g5Q1EPG4Wsnxqhbl0luFVRyX5dbKF3MQstkoWawSNKLulS2WsZZbALsPUX4Ax6SY9faqEs6dPM47cBAcg==} - dev: true - /@types/scheduler@0.16.8: resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} dev: true @@ -1613,10 +1602,6 @@ packages: '@types/node': 20.10.4 dev: true - /@types/sizzle@2.3.8: - resolution: {integrity: sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==} - dev: true - /@types/source-list-map@0.1.6: resolution: {integrity: sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==} @@ -2049,7 +2034,7 @@ packages: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - dev: true + dev: false /acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} @@ -2447,11 +2432,6 @@ packages: tweetnacl: 0.14.5 dev: false - /big-integer@1.6.52: - resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} - engines: {node: '>=0.6'} - dev: true - /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -2460,13 +2440,6 @@ packages: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: false - /bplist-parser@0.2.0: - resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} - engines: {node: '>= 5.10.0'} - dependencies: - big-integer: 1.6.52 - dev: true - /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -2509,17 +2482,10 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - /bundle-name@3.0.0: - resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} - engines: {node: '>=12'} - dependencies: - run-applescript: 5.0.0 - dev: true - /bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - dev: true + dev: false /cache-content-type@1.0.1: resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} @@ -2527,7 +2493,7 @@ packages: dependencies: mime-types: 2.1.35 ylru: 1.3.2 - dev: true + dev: false /call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} @@ -2680,12 +2646,11 @@ packages: qs: 6.5.3 raw-body: 2.5.2 type-is: 1.6.18 - dev: true + dev: false /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true /collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} @@ -2775,12 +2740,12 @@ packages: engines: {node: '>= 0.6'} dependencies: safe-buffer: 5.2.1 - dev: true + dev: false /content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - dev: true + dev: false /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2792,11 +2757,11 @@ packages: dependencies: depd: 2.0.0 keygrip: 1.1.0 - dev: true + dev: false /copy-to@2.0.1: resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==} - dev: true + dev: false /copy-webpack-plugin@11.0.0(webpack@5.89.0): resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} @@ -2838,7 +2803,7 @@ packages: typescript: 5.3.3 dev: true - /create-jest@29.7.0(@types/node@20.10.4)(ts-node@10.9.2): + /create-jest@29.7.0(@types/node@18.19.9)(ts-node@10.9.2): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -2847,7 +2812,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -2988,7 +2953,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -3005,7 +2969,7 @@ packages: /deep-equal@1.0.1: resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} - dev: true + dev: false /deep-equal@2.2.3: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} @@ -3039,24 +3003,6 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - /default-browser-id@3.0.0: - resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} - engines: {node: '>=12'} - dependencies: - bplist-parser: 0.2.0 - untildify: 4.0.0 - dev: true - - /default-browser@4.0.0: - resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} - engines: {node: '>=14.16'} - dependencies: - bundle-name: 3.0.0 - default-browser-id: 3.0.0 - execa: 7.2.0 - titleize: 3.0.0 - dev: true - /define-data-property@1.1.1: resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} engines: {node: '>= 0.4'} @@ -3066,11 +3012,6 @@ packages: has-property-descriptors: 1.0.1 dev: true - /define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - dev: true - /define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -3099,17 +3040,17 @@ packages: /delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: true + dev: false /depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} - dev: true + dev: false /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - dev: true + dev: false /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} @@ -3119,7 +3060,7 @@ packages: /destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: true + dev: false /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} @@ -3238,11 +3179,6 @@ packages: tslib: 2.6.2 dev: false - /dotenv@16.3.1: - resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} - engines: {node: '>=12'} - dev: false - /duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -3255,7 +3191,7 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - dev: true + dev: false /electron-to-chromium@1.4.607: resolution: {integrity: sha512-YUlnPwE6eYxzwBnFmawA8LiLRfm70R2aJRIUv0n03uHt/cUzzYACOogmvk8M2+hVzt/kB80KJXx7d5f5JofPvQ==} @@ -3276,7 +3212,7 @@ packages: /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} - dev: true + dev: false /enhanced-resolve@5.15.0: resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} @@ -3410,34 +3346,35 @@ packages: is-symbol: 1.0.4 dev: true - /esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + /esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 dev: true /escalade@3.1.1: @@ -3446,7 +3383,7 @@ packages: /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: true + dev: false /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} @@ -3509,15 +3446,6 @@ packages: object.entries: 1.1.7 dev: true - /eslint-config-prettier@9.1.0(eslint@8.55.0): - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - dependencies: - eslint: 8.55.0 - dev: true - /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: @@ -3567,15 +3495,6 @@ packages: eslint-rule-composer: 0.3.0 dev: true - /eslint-plugin-cypress@2.15.1(eslint@8.55.0): - resolution: {integrity: sha512-eLHLWP5Q+I4j2AWepYq0PgFEei9/s5LvjuSqWrxurkg1YZ8ltxdvMNmdSf0drnsNo57CTgYY/NIHHLRSWejR7w==} - peerDependencies: - eslint: '>= 3.2.1' - dependencies: - eslint: 8.55.0 - globals: 13.23.0 - dev: true - /eslint-plugin-es@3.0.1(eslint@8.55.0): resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} engines: {node: '>=8.10.0'} @@ -3628,12 +3547,12 @@ packages: - supports-color dev: true - /eslint-plugin-jest@26.9.0(@typescript-eslint/eslint-plugin@6.13.2)(eslint@8.55.0)(jest@29.7.0)(typescript@5.3.3): - resolution: {integrity: sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /eslint-plugin-jest@27.6.3(@typescript-eslint/eslint-plugin@6.13.2)(eslint@8.55.0)(jest@29.7.0)(typescript@5.3.3): + resolution: {integrity: sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: - '@typescript-eslint/eslint-plugin': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/eslint-plugin': ^5.0.0 || ^6.0.0 + eslint: ^7.0.0 || ^8.0.0 jest: '*' peerDependenciesMeta: '@typescript-eslint/eslint-plugin': @@ -3644,7 +3563,7 @@ packages: '@typescript-eslint/eslint-plugin': 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3) '@typescript-eslint/utils': 5.62.0(eslint@8.55.0)(typescript@5.3.3) eslint: 8.55.0 - jest: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + jest: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) transitivePeerDependencies: - supports-color - typescript @@ -3690,27 +3609,6 @@ packages: semver: 6.3.1 dev: true - /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.0): - resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} - engines: {node: ^14.18.0 || >=16.0.0} - 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 - dependencies: - eslint: 8.55.0 - eslint-config-prettier: 9.1.0(eslint@8.55.0) - prettier: 3.1.0 - prettier-linter-helpers: 1.0.0 - synckit: 0.8.6 - dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.55.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} @@ -3913,21 +3811,6 @@ packages: strip-final-newline: 2.0.0 dev: true - /execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.1.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - dev: true - /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -3956,10 +3839,6 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true - /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -4081,7 +3960,7 @@ packages: /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - dev: true + dev: false /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -4308,14 +4187,12 @@ packages: /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} - dev: true /has-tostringtag@1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 - dev: true /hasown@2.0.0: resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} @@ -4390,7 +4267,7 @@ packages: dependencies: deep-equal: 1.0.1 http-errors: 1.8.1 - dev: true + dev: false /http-errors@1.8.1: resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} @@ -4401,7 +4278,7 @@ packages: setprototypeof: 1.2.0 statuses: 1.5.0 toidentifier: 1.0.1 - dev: true + dev: false /http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} @@ -4412,11 +4289,11 @@ packages: setprototypeof: 1.2.0 statuses: 2.0.1 toidentifier: 1.0.1 - dev: true + dev: false /http-message-signatures@0.1.2: resolution: {integrity: sha512-gjJYDgFBy+xnlAs2G0gIWpiorCv9Xi7pIlOnnd91zHAK7BtkLxonmm/JAtd5e6CakOuW03IwEuJzj2YMy8lfWQ==} - dev: true + dev: false /http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} @@ -4442,7 +4319,7 @@ packages: resolution: {integrity: sha512-RpaFuZD3tG8wtsvjDv1u7O8wAvfpAxS20F3CrFPZOMn+IBb7E7yiqlN5Tks4E5tBEnTdpMOD151rJsUv03sAIg==} dependencies: structured-headers: 0.5.0 - dev: true + dev: false /https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} @@ -4459,17 +4336,12 @@ packages: engines: {node: '>=10.17.0'} dev: true - /human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} - dev: true - /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 - dev: true + dev: false /iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} @@ -4524,7 +4396,7 @@ packages: /inflation@2.1.0: resolution: {integrity: sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==} engines: {node: '>= 0.8.0'} - dev: true + dev: false /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} @@ -4612,18 +4484,6 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - dev: true - - /is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - dev: true - /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -4649,7 +4509,6 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -4657,14 +4516,6 @@ packages: dependencies: is-extglob: 2.1.1 - /is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - dependencies: - is-docker: 3.0.0 - dev: true - /is-map@2.0.2: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} dev: true @@ -4746,11 +4597,6 @@ packages: engines: {node: '>=8'} dev: true - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -4793,13 +4639,6 @@ packages: get-intrinsic: 1.2.2 dev: true - /is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - dependencies: - is-docker: 2.2.1 - dev: true - /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} dev: true @@ -4899,7 +4738,7 @@ packages: jest: ^26.0.1 || ^27.0.0 dependencies: '@types/chrome': 0.0.114 - jest: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + jest: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) dev: true /jest-circus@29.7.0: @@ -4931,7 +4770,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.10.4)(ts-node@10.9.2): + /jest-cli@29.7.0(@types/node@18.19.9)(ts-node@10.9.2): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -4945,10 +4784,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + create-jest: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -4959,6 +4798,47 @@ packages: - ts-node dev: true + /jest-config@29.7.0(@types/node@18.19.9)(ts-node@10.9.2): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.23.5 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.9 + babel-jest: 29.7.0(@babel/core@7.23.5) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.2(@types/node@18.19.9)(typescript@5.3.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + /jest-config@29.7.0(@types/node@20.10.4)(ts-node@10.9.2): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4994,7 +4874,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.2(@types/node@20.10.4)(typescript@5.3.3) + ts-node: 10.9.2(@types/node@18.19.9)(typescript@5.3.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -5318,7 +5198,7 @@ packages: supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.10.4)(ts-node@10.9.2): + /jest@29.7.0(@types/node@18.19.9)(ts-node@10.9.2): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -5331,7 +5211,7 @@ packages: '@jest/core': 29.7.0(ts-node@10.9.2) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + jest-cli: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -5346,7 +5226,7 @@ packages: /jose@4.15.4: resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==} - dev: true + dev: false /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -5480,7 +5360,7 @@ packages: engines: {node: '>= 0.6'} dependencies: tsscmp: 1.0.6 - dev: true + dev: false /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -5504,11 +5384,11 @@ packages: co-body: 6.1.0 copy-to: 2.0.1 type-is: 1.6.18 - dev: true + dev: false /koa-compose@4.1.0: resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} - dev: true + dev: false /koa-convert@2.0.0: resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} @@ -5516,7 +5396,7 @@ packages: dependencies: co: 4.6.0 koa-compose: 4.1.0 - dev: true + dev: false /koa@2.14.2: resolution: {integrity: sha512-VFI2bpJaodz6P7x2uyLiX6RLYpZmOJqNmoCst/Yyd7hQlszyPwG/I9CQJ63nOtKSxpt5M7NH67V6nJL2BwCl7g==} @@ -5547,7 +5427,7 @@ packages: vary: 1.1.2 transitivePeerDependencies: - supports-color - dev: true + dev: false /language-subtag-registry@0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} @@ -5671,7 +5551,7 @@ packages: /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - dev: true + dev: false /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -5702,11 +5582,6 @@ packages: engines: {node: '>=6'} dev: true - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true - /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -5731,7 +5606,6 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -5757,7 +5631,7 @@ packages: /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} - dev: true + dev: false /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -5792,13 +5666,6 @@ packages: path-key: 3.1.1 dev: true - /npm-run-path@5.1.0: - resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - path-key: 4.0.0 - dev: true - /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: @@ -5897,7 +5764,7 @@ packages: engines: {node: '>= 0.8'} dependencies: ee-first: 1.1.1 - dev: true + dev: false /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -5911,26 +5778,9 @@ packages: mimic-fn: 2.1.0 dev: true - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - dependencies: - mimic-fn: 4.0.0 - dev: true - /only@0.0.2: resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} - dev: true - - /open@9.1.0: - resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} - engines: {node: '>=14.16'} - dependencies: - default-browser: 4.0.0 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 2.2.0 - dev: true + dev: false /opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} @@ -6021,7 +5871,7 @@ packages: /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - dev: true + dev: false /pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} @@ -6046,11 +5896,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true - /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -6137,7 +5982,7 @@ packages: dependencies: lilconfig: 3.0.0 postcss: 8.4.32 - ts-node: 10.9.2(@types/node@20.10.4)(typescript@5.3.3) + ts-node: 10.9.2(@types/node@18.19.9)(typescript@5.3.3) yaml: 2.3.4 dev: true @@ -6231,11 +6076,56 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} + /prettier-plugin-tailwindcss@0.5.11(prettier@3.1.0): + resolution: {integrity: sha512-AvI/DNyMctyyxGOjyePgi/gqj5hJYClZ1avtQvLlqMT3uDZkRbi4HhGUpok3DRzv9z7Lti85Kdj3s3/1CeNI0w==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + prettier-plugin-twig-melody: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier-plugin-twig-melody: + optional: true dependencies: - fast-diff: 1.3.0 + prettier: 3.1.0 dev: true /prettier@3.1.0: @@ -6283,6 +6173,7 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + dev: true /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -6306,6 +6197,7 @@ packages: /qs@6.5.3: resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} engines: {node: '>=0.6'} + dev: false /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -6327,7 +6219,7 @@ packages: http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: true + dev: false /react-dom@18.2.0(react@18.2.0): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} @@ -6340,6 +6232,7 @@ packages: /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -6360,7 +6253,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-router: 6.21.1(react@18.2.0) - dev: true + dev: false /react-router@6.21.1(react@18.2.0): resolution: {integrity: sha512-W0l13YlMTm1YrpVIOpjCADJqEUpz1vm+CMo47RuFX4Ftegwm6KOYsL5G3eiE52jnJpKvzm6uB/vTKTPKM8dmkA==} @@ -6370,7 +6263,7 @@ packages: dependencies: '@remix-run/router': 1.14.1 react: 18.2.0 - dev: true + dev: false /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} @@ -6547,13 +6440,6 @@ packages: glob: 7.2.3 dev: true - /run-applescript@5.0.0: - resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} - engines: {node: '>=12'} - dependencies: - execa: 5.1.1 - dev: true - /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -6695,7 +6581,7 @@ packages: /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: true + dev: false /shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} @@ -6810,12 +6696,12 @@ packages: /statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} - dev: true + dev: false /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - dev: true + dev: false /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} @@ -6901,11 +6787,6 @@ packages: engines: {node: '>=6'} dev: true - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true - /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -6920,7 +6801,7 @@ packages: /structured-headers@0.5.0: resolution: {integrity: sha512-oLnmXSsjhud+LxRJpvokwP8ImEB2wTg8sg30buwfVViKMuluTv3BlOJHUX9VW9pJ2nQOxmx87Z0kB86O4cphag==} - dev: true + dev: false /style-loader@3.3.3(webpack@5.89.0): resolution: {integrity: sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==} @@ -6973,14 +6854,6 @@ packages: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true - /synckit@0.8.6: - resolution: {integrity: sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==} - engines: {node: ^14.18.0 || >=16.0.0} - dependencies: - '@pkgr/utils': 2.4.2 - tslib: 2.6.2 - dev: true - /tailwind-merge@2.1.0: resolution: {integrity: sha512-l11VvI4nSwW7MtLSLYT4ldidDEUwQAMWuSHk7l4zcXZDgnCRa0V3OdCwFfM7DCzakVXMNRwAeje9maFFXT71dQ==} dependencies: @@ -7081,11 +6954,6 @@ packages: any-promise: 1.3.0 dev: true - /titleize@3.0.0: - resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} - engines: {node: '>=12'} - dev: true - /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -7111,7 +6979,7 @@ packages: /toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - dev: true + dev: false /totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} @@ -7184,7 +7052,7 @@ packages: '@babel/core': 7.23.5 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2) + jest: 29.7.0(@types/node@18.19.9)(ts-node@10.9.2) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -7210,7 +7078,7 @@ packages: webpack: 5.89.0(webpack-cli@5.1.4) dev: true - /ts-node@10.9.2(@types/node@20.10.4)(typescript@5.3.3): + /ts-node@10.9.2(@types/node@18.19.9)(typescript@5.3.3): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -7229,7 +7097,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.10.4 + '@types/node': 18.19.9 acorn: 8.11.2 acorn-walk: 8.3.1 arg: 4.1.3 @@ -7260,7 +7128,7 @@ packages: /tsscmp@1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} engines: {node: '>=0.6.x'} - dev: true + dev: false /tsutils@3.21.0(typescript@5.3.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -7272,12 +7140,12 @@ packages: typescript: 5.3.3 dev: true - /tsx@4.6.2: - resolution: {integrity: sha512-QPpBdJo+ZDtqZgAnq86iY/PD2KYCUPSUGIunHdGwyII99GKH+f3z3FZ8XNFLSGQIA4I365ui8wnQpl8OKLqcsg==} + /tsx@4.7.0: + resolution: {integrity: sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==} engines: {node: '>=18.0.0'} hasBin: true dependencies: - esbuild: 0.18.20 + esbuild: 0.19.12 get-tsconfig: 4.7.2 optionalDependencies: fsevents: 2.3.3 @@ -7321,7 +7189,7 @@ packages: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: true + dev: false /typed-array-buffer@1.0.0: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} @@ -7387,12 +7255,7 @@ packages: /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - dev: true - - /untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - dev: true + dev: false /update-browserslist-db@1.0.13(browserslist@4.22.2): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} @@ -7442,6 +7305,7 @@ packages: /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + dev: false /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -7459,7 +7323,7 @@ packages: /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - dev: true + dev: false /verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} @@ -7830,7 +7694,7 @@ packages: /ylru@1.3.2: resolution: {integrity: sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==} engines: {node: '>= 4.0.0'} - dev: true + dev: false /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..18ec407e --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'packages/*' diff --git a/scripts/dev.sh b/scripts/dev.sh deleted file mode 100755 index 220fa448..00000000 --- a/scripts/dev.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -u - -set -ex - -TARGET=${1:-chrome} -NODE_ENV=development - -echo Running ${NODE_ENV} build for ${TARGET}... -pnpm concurrently -n SIGNATURES,EXTENSION -c green.bold,blue.bold "pnpm local-signatures" "NODE_ENV=${NODE_ENV} TARGET=${TARGET} webpack" diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json deleted file mode 100755 index b22d4478..00000000 --- a/src/_locales/en/messages.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "appName": { - "message": "Web Monetization", - "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website." - }, - "appDescription": { - "message": "Web Monetization Extension", - "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website." - }, - "btnTooltip": { - "message": "Web Monetization", - "description": "..." - } -} diff --git a/src/background/config.ts b/src/background/config.ts deleted file mode 100644 index 2dbd68af..00000000 --- a/src/background/config.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare const CONFIG_SIGNATURES_URL: string -declare const CONFIG_WM_WALLET_ADDRESS: string - -export const SIGNATURES_URL = CONFIG_SIGNATURES_URL -export const WM_WALLET_ADDRESS = CONFIG_WM_WALLET_ADDRESS diff --git a/src/components/__tests__/radio-group.test.tsx b/src/components/__tests__/radio-group.test.tsx deleted file mode 100644 index f152c112..00000000 --- a/src/components/__tests__/radio-group.test.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { fireEvent, render } from '@testing-library/react' -import React from 'react' - -import { Radio, RadioGroup } from '../radio-group' - -describe('RadioGroup', () => { - const radioItems = [ - { label: 'Option 1', value: 'option1', checked: true }, - { label: 'Option 2', value: 'option2' }, - ] - - it('should have the `flex-row` class when the `inline` variant is passed', () => { - const { queryByRole } = render( - , - ) - - expect(queryByRole('radiogroup')).toBeInTheDocument() - expect(queryByRole('radiogroup')).toHaveClass('flex-row') - }) - - it('renders radio group correctly with items', () => { - const { getByRole } = render() - - const radioGroup = getByRole('radiogroup') - expect(radioGroup).toBeInTheDocument() - expect(radioGroup.childNodes.length).toBe(2) // Ensure two radio buttons are rendered - }) - - it('renders radio group with no element checked by default', () => { - const radioItemsNotChecked = [ - { label: 'Option 1', value: 'option1' }, - { label: 'Option 2', value: 'option2' }, - ] - const { getByLabelText } = render() - - const firstRadioButton = getByLabelText('Option 1') - const secondRadioButton = getByLabelText('Option 2') - - expect(firstRadioButton).not.toBeChecked() - expect(secondRadioButton).not.toBeChecked() - }) - - it('handles keyboard navigation', () => { - const { getByLabelText } = render() - - const radioGroup = getByLabelText('Option 1') - fireEvent.keyDown(radioGroup, { key: 'ArrowRight', code: 'ArrowRight' }) - let secondRadioButton = getByLabelText('Option 2') - expect(secondRadioButton).toBeChecked() - - fireEvent.keyDown(radioGroup, { key: 'ArrowLeft', code: 'ArrowLeft' }) - let firstRadioButton = getByLabelText('Option 1') - expect(firstRadioButton).toBeChecked() - - fireEvent.keyDown(radioGroup, { key: 'ArrowUp', code: 'ArrowUp' }) - secondRadioButton = getByLabelText('Option 2') - expect(secondRadioButton).toBeChecked() - - fireEvent.keyDown(radioGroup, { key: 'ArrowDown', code: 'ArrowDown' }) - firstRadioButton = getByLabelText('Option 1') - expect(firstRadioButton).toBeChecked() - }) - - it('changes selection on arrow keys', () => { - const { getByLabelText } = render() - - const radioGroup = getByLabelText('Option 1') - fireEvent.keyDown(radioGroup, { key: 'ArrowRight', code: 'ArrowRight' }) - fireEvent.keyDown(radioGroup, { key: 'Enter', code: 'Enter' }) - const secondRadioButton = getByLabelText('Option 2') - expect(secondRadioButton).toBeChecked() - }) - - it('changes selection on clicking radio buttons', () => { - const { getByLabelText } = render() - - const secondRadioButton = getByLabelText('Option 2') - fireEvent.click(secondRadioButton) - expect(secondRadioButton).toBeChecked() - }) -}) - -describe('Radio', () => { - it('renders radio button correctly with label', () => { - const { getByLabelText } = render() - - const radioButton = getByLabelText('Option 1') - expect(radioButton).toBeInTheDocument() - expect(radioButton).toHaveAttribute('type', 'radio') - expect(radioButton).not.toBeChecked() - - fireEvent.click(radioButton) - expect(radioButton).toBeChecked() - }) - - it('renders disabled radio button', () => { - const { getByLabelText } = render( - , - ) - - const radioButton = getByLabelText('Option 1') - expect(radioButton).toBeDisabled() - }) -}) diff --git a/src/components/__tests__/switch.test.tsx b/src/components/__tests__/switch.test.tsx deleted file mode 100644 index 7c509b11..00000000 --- a/src/components/__tests__/switch.test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import '@testing-library/jest-dom' - -import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' - -import { Switch } from '@/components/switch' - -describe('Switch', () => { - it('renders without crashing', () => { - render() - expect(screen.getByRole('switch')).toBeInTheDocument() - }) - - it('applies default classes', () => { - render() - const switchElement = screen.getByRole('switch').nextSibling - expect(switchElement).toHaveClass('w-[42px] h-[26px] before:h-5 before:w-5') - }) - - it('applies small size classes when size prop is small', () => { - render() - const switchElement = screen.getByRole('switch').nextSibling - expect(switchElement).toHaveClass('w-9 h-[22px] before:h-4 before:w-4 before:left-[3px]') - }) - - it('forwards ref to input element', () => { - const ref = React.createRef() - render() - expect(ref.current).toBeInstanceOf(HTMLInputElement) - }) - - it('forwards checked prop to input element', () => { - render() - const inputElement = screen.getByRole('switch') - expect(inputElement).toBeChecked() - }) - - it('handles additional props', () => { - render() - const inputElement = screen.getByRole('switch') - expect(inputElement).toHaveAttribute('aria-label', 'Custom Switch') - }) - - it('applies custom class names', () => { - const customClass = 'custom-class' - render() - const switchElement = screen.getByRole('switch').nextSibling - expect(switchElement).toHaveClass(customClass) - }) - - it('toggles switch state when clicked', () => { - render() - const inputElement = screen.getByRole('switch') - expect(inputElement).not.toBeChecked() - - fireEvent.click(inputElement) - expect(inputElement).toBeChecked() - - fireEvent.click(inputElement) - expect(inputElement).not.toBeChecked() - }) - - it('handles additional HTML attributes', () => { - const testId = 'switch-test' - render() - const switchElement = screen.getByTestId(testId) - expect(switchElement).toBeInTheDocument() - }) -}) diff --git a/src/content/index.tsx b/src/content/index.tsx deleted file mode 100755 index 56d02f73..00000000 --- a/src/content/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { runtime } from 'webextension-polyfill' - -import { initMonetizationTagManager } from '@/utils/monetizationTagManager' -import { wm2Polyfill } from '@/utils/polyfill' - -import { loadObserver } from './linksObserver' -import MessageListener from './messageListener' - -// import "./content.css"; - -runtime.onMessage.addListener(MessageListener) - -function inject(configure: (_script: HTMLScriptElement) => void) { - const script = document.createElement('script') - configure(script) - document.documentElement.appendChild(script) - // document.documentElement.removeChild(script) -} - -// eslint-disable-next-line @typescript-eslint/no-extra-semi -;(function injectCode(code: string) { - inject(script => (script.innerHTML = code)) -})(wm2Polyfill) - -loadObserver() -initMonetizationTagManager() diff --git a/src/manifest/chrome-v3.json b/src/manifest/chrome-v3.json deleted file mode 100644 index 622b6a88..00000000 --- a/src/manifest/chrome-v3.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "__MSG_appName__", - "version": "1.0.1", - "manifest_version": 3, - "description": "__MSG_appDescription__", - "icons": { - "34": "assets/icons/icon-34.png", - "128": "assets/icons/icon-128.png" - }, - "default_locale": "en", - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*"], - "js": ["content/content.js"] - } - ], - "background": { - "service_worker": "background/background.js" - }, - "permissions": ["tabs", "storage"], - "host_permissions": ["http://*/*", "https://*/*"], - "options_ui": { - "page": "options/index.html" - }, - "action": { - "default_icon": { - "16": "assets/icons/icon-16.png", - "48": "assets/icons/icon-48.png" - }, - "default_title": "Web Monetization", - "default_popup": "popup/index.html" - }, - "web_accessible_resources": [ - { - "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], - "matches": [""] - } - ] -} diff --git a/src/manifest/edge.json b/src/manifest/edge.json deleted file mode 100644 index dfa03fcb..00000000 --- a/src/manifest/edge.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "__MSG_appName__", - "version": "1.0.1", - "manifest_version": 3, - "description": "__MSG_appDescription__", - "icons": { - "34": "assets/icons/icon-34.png", - "128": "assets/icons/icon-128.png" - }, - "default_locale": "en", - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*"], - "js": ["content/content.js"] - } - ], - "background": { - "service_worker": "background/background.js" - }, - "host_permissions": ["http://*/*", "https://*/*"], - "web_accessible_resources": [ - { - "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], - "matches": [""] - } - ] -} diff --git a/src/manifest/firefox-v3.json b/src/manifest/firefox-v3.json deleted file mode 100644 index ee9ae7e4..00000000 --- a/src/manifest/firefox-v3.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "__MSG_appName__", - "version": "1.0.1", - "manifest_version": 3, - "description": "__MSG_appDescription__", - "icons": { - "34": "assets/icons/icon-34.png", - "128": "assets/icons/icon-128.png" - }, - "default_locale": "en", - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*"], - "js": ["content/content.js"] - } - ], - "background": { - "scripts": ["background/background.js"], - "type": "module" - }, - "permissions": ["tabs", "storage"], - "host_permissions": ["http://*/*", "https://*/*"], - "options_ui": { - "page": "options/index.html", - "browser_style": false - }, - "action": { - "default_icon": { - "16": "assets/icons/icon-16.png", - "48": "assets/icons/icon-48.png" - }, - "default_title": "Web Monetization", - "default_popup": "popup/index.html", - "browser_style": false - }, - "web_accessible_resources": [ - { - "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], - "matches": [""] - } - ], - "browser_specific_settings": { - "gecko": { - "id": "web-extension@interledger.com" - } - } -} diff --git a/src/manifest/firefox.json b/src/manifest/firefox.json deleted file mode 100644 index 14232657..00000000 --- a/src/manifest/firefox.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "__MSG_appName__", - "version": "1.0.1", - "manifest_version": 2, - "description": "__MSG_appDescription__", - "icons": { - "34": "assets/icons/icon-34.png", - "128": "assets/icons/icon-128.png" - }, - "default_locale": "en", - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*", ""], - "js": ["content/content.js"] - } - ], - "background": { - "scripts": ["background/background.js"], - "type": "module" - }, - "permissions": ["tabs", "storage"], - "browser_action": { - "default_icon": "assets/icons/icon-34.png", - "default_title": "Web Monetization", - "default_popup": "popup/index.html", - "browser_style": false - }, - "web_accessible_resources": [ - "assets/*", - "content/*", - "options/*", - "popup/*", - "background/*" - ], - "browser_specific_settings": { - "gecko": { - "id": "web-extension@interledger.com" - } - } -} diff --git a/src/manifest/opera.json b/src/manifest/opera.json deleted file mode 100644 index dfa03fcb..00000000 --- a/src/manifest/opera.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "__MSG_appName__", - "version": "1.0.1", - "manifest_version": 3, - "description": "__MSG_appDescription__", - "icons": { - "34": "assets/icons/icon-34.png", - "128": "assets/icons/icon-128.png" - }, - "default_locale": "en", - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*"], - "js": ["content/content.js"] - } - ], - "background": { - "service_worker": "background/background.js" - }, - "host_permissions": ["http://*/*", "https://*/*"], - "web_accessible_resources": [ - { - "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"], - "matches": [""] - } - ] -} diff --git a/src/popup/Popup.tsx b/src/popup/Popup.tsx deleted file mode 100644 index 89d305cf..00000000 --- a/src/popup/Popup.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import './Popup.scss' - -import React from 'react' - -import { RouterProvider } from '@/components/router-provider' - -const Popup = () => { - return -} - -export default Popup diff --git a/src/popup/index.html b/src/popup/index.html deleted file mode 100755 index 5f106fb3..00000000 --- a/src/popup/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - <%= htmlWebpackPlugin.options.title %> - - - - - - - - diff --git a/src/popup/index.tsx b/src/popup/index.tsx deleted file mode 100755 index e45924bd..00000000 --- a/src/popup/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import './index.css' - -import React from 'react' -import ReactDOM from 'react-dom/client' - -import Popup from './Popup' - -const root = ReactDOM.createRoot(document.getElementById('popup-container')!) -root.render() diff --git a/src/popup/pages/Settings.tsx b/src/popup/pages/Settings.tsx deleted file mode 100644 index eacdd66a..00000000 --- a/src/popup/pages/Settings.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react' - -export const Settings = () => { - return <>Settings -} diff --git a/src/types/openControl.d.ts b/src/types/openControl.d.ts deleted file mode 100644 index 1c20b8ce..00000000 --- a/src/types/openControl.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare type OpenControls = { - isVisible: boolean - handleClose: () => void - handleOpen: () => void - handleToggle: () => void - isOpen: boolean -} diff --git a/src/utils/cn.ts b/src/utils/cn.ts deleted file mode 100644 index ef859784..00000000 --- a/src/utils/cn.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { cx, CxOptions } from 'class-variance-authority' -import { twMerge } from 'tailwind-merge' - -export function cn(...inputs: CxOptions) { - return twMerge(cx(inputs)) -} diff --git a/src/utils/monetizationTagManager/mozClone.ts b/src/utils/monetizationTagManager/mozClone.ts deleted file mode 100644 index dc4f8fe3..00000000 --- a/src/utils/monetizationTagManager/mozClone.ts +++ /dev/null @@ -1,14 +0,0 @@ -type DefaultView = WindowProxy & typeof globalThis -type CloneInto = (obj: unknown, _window: DefaultView | null) => typeof obj -declare const cloneInto: CloneInto | undefined - -let cloneIntoRef: CloneInto | undefined -try { - cloneIntoRef = cloneInto -} catch (e) { - cloneIntoRef = undefined -} - -export function mozClone(obj: unknown, document: Document) { - return cloneIntoRef ? cloneIntoRef(obj, document.defaultView) : obj -} diff --git a/src/utils/storage.ts b/src/utils/storage.ts deleted file mode 100755 index 24995aeb..00000000 --- a/src/utils/storage.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { storage } from 'webextension-polyfill' - -export default storage.sync ? storage.sync : storage.local diff --git a/tsconfig.json b/tsconfig.json index a568303a..f7513afa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,30 +1,11 @@ { - "compilerOptions": { - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "baseUrl": "./src", - "esModuleInterop": true, - "module": "commonjs", - "target": "es5", - "allowJs": true, - "jsx": "react", - "sourceMap": true, - "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "allowUmdGlobalAccess": true, - "resolveJsonModule": true, - "paths": { - "@/utils/*": ["./utils/*"], - "@/popup/*": ["./popup/*"], - "@/background/*": ["./background/*"], - "@/content/*": ["./content/*"], - "@/assets/*": ["./assets/*"], - "@/components/*": ["./components/*"], - "@/types/*": ["./types/*"], - "@/hooks/*": ["./hooks/*"] + "references": [ + { + "path": "packages/extension" + }, + { + "path": "packages/server" } - }, - "include": ["./src/**/*", "local-signature/**/*", "./jest.config.ts", "./jest.setup.ts"], - "exclude": ["dist", "dev", "temp"] + ], + "files": [] }