├── .github ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── config.yml ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── dependabot-auto-merge.yml │ ├── fix-php-code-style-issues.yml │ ├── phpstan.yml │ └── run-tests.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── UPGRADING.md ├── composer.json ├── config └── sign-pad.php ├── database └── migrations │ └── create_signatures_table.php.stub ├── package-lock.json ├── package.json ├── phpstan.neon.dist ├── phpunit.xml.dist ├── resources ├── assets │ └── sign-pad.js ├── dist │ ├── sign-pad.min.js │ └── sign-pad.min.js.LICENSE.txt └── views │ ├── components │ └── signature-pad.blade.php │ ├── pad.blade.php │ ├── pdf.blade.php │ └── template │ └── pdf.blade.php ├── routes └── web.php ├── src ├── Actions │ ├── AppendSignatureDocumentAction.php │ ├── CertifyDocumentAction.php │ └── GenerateSignatureDocumentAction.php ├── Commands │ └── InstallCommand.php ├── Components │ └── SignaturePad.php ├── Concerns │ └── RequiresSignature.php ├── Contracts │ ├── CanBeSigned.php │ └── ShouldGenerateSignatureDocument.php ├── Controllers │ └── LaravelSignPadController.php ├── Exceptions │ ├── InvalidConfiguration.php │ └── ModelHasAlreadyBeenSigned.php ├── LaravelSignPadServiceProvider.php ├── Signature.php ├── SignatureDocumentTemplate.php ├── SignaturePosition.php └── Templates │ ├── BladeDocumentTemplate.php │ ├── DocumentTemplate.php │ └── PdfDocumentTemplate.php ├── tests ├── Feature │ ├── RequiresSignatureTest.php │ ├── SignPadComponentTest.php │ └── SignPadControllerTest.php ├── Models │ └── TestModel.php ├── Pest.php ├── TestCase.php ├── TestClasses │ └── TestSignature.php └── migrations │ └── create_test_models_table.php.stub └── webpack.config.js /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code 10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be 11 | extremely unfair for them to suffer abuse or anger for their hard work. 12 | 13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the 14 | world that developers are civilized and selfless people. 15 | 16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient 17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 18 | 19 | ## Viability 20 | 21 | When requesting or submitting new features, first consider whether it might be useful to others. Open 22 | source projects are used by many developers, who may have entirely different needs to your own. Think about 23 | whether or not your feature is likely to be used by other users of the project. 24 | 25 | ## Procedure 26 | 27 | Before filing an issue: 28 | 29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 30 | - Check to make sure your feature suggestion isn't already present within the project. 31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 32 | - Check the pull requests tab to ensure that the feature isn't already in progress. 33 | 34 | Before submitting a pull request: 35 | 36 | - Check the codebase to ensure that your feature doesn't already exist. 37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 38 | 39 | ## Requirements 40 | 41 | If the project maintainer has any additional requirements, you will find them listed here. 42 | 43 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). 44 | 45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 46 | 47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 48 | 49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 50 | 51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 52 | 53 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: creagia 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/creagia/laravel-sign-pad/discussions/new?category=q-a 5 | about: Ask the community for help 6 | - name: Request a feature 7 | url: https://github.com/creagia/laravel-sign-pad/discussions/new?category=ideas 8 | about: Share ideas for new features 9 | - name: Report a security issue 10 | url: https://github.com/creagia/laravel-sign-pad/security/policy 11 | about: Learn how to notify us for sensitive bugs 12 | - name: Report a bug 13 | url: https://github.com/creagia/laravel-sign-pad/issues/new 14 | about: Report a reproducable bug 15 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you discover any security related issues, please email david@creagia.com instead of using the issue tracker. 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | labels: 12 | - "dependencies" -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: dependabot-auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | 14 | - name: Dependabot metadata 15 | id: metadata 16 | uses: dependabot/fetch-metadata@v2.4.0 17 | with: 18 | github-token: "${{ secrets.GITHUB_TOKEN }}" 19 | 20 | - name: Auto-merge Dependabot PRs for semver-minor updates 21 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} 22 | run: gh pr merge --auto --merge "$PR_URL" 23 | env: 24 | PR_URL: ${{github.event.pull_request.html_url}} 25 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 26 | 27 | - name: Auto-merge Dependabot PRs for semver-patch updates 28 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 29 | run: gh pr merge --auto --merge "$PR_URL" 30 | env: 31 | PR_URL: ${{github.event.pull_request.html_url}} 32 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 33 | -------------------------------------------------------------------------------- /.github/workflows/fix-php-code-style-issues.yml: -------------------------------------------------------------------------------- 1 | name: Fix PHP code style issues 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.php' 7 | 8 | jobs: 9 | php-code-styling: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | with: 16 | ref: ${{ github.head_ref }} 17 | 18 | - name: Fix PHP code style issues 19 | uses: aglipanci/laravel-pint-action@2.5 20 | 21 | - name: Commit changes 22 | uses: stefanzweifel/git-auto-commit-action@v5 23 | with: 24 | commit_message: Fix styling 25 | -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: PHPStan 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.php' 7 | - 'phpstan.neon.dist' 8 | 9 | jobs: 10 | phpstan: 11 | name: phpstan 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup PHP 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: '8.1' 20 | coverage: none 21 | 22 | - name: Install composer dependencies 23 | uses: ramsey/composer-install@v3 24 | 25 | - name: Run PHPStan 26 | run: ./vendor/bin/phpstan --error-format=github 27 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | runs-on: ${{ matrix.os }} 14 | 15 | strategy: 16 | fail-fast: true 17 | matrix: 18 | os: [ubuntu-latest] 19 | php: [8.4, 8.3, 8.2, 8.1, 8.0] 20 | laravel: ['8.*', '9.*', '10.*', '11.*', '12.*'] 21 | stability: [prefer-stable] 22 | include: 23 | - laravel: 11.* 24 | testbench: 9.* 25 | - laravel: 10.* 26 | testbench: 8.* 27 | - laravel: 9.* 28 | testbench: 7.* 29 | - laravel: 8.* 30 | testbench: 6.23 31 | - laravel: 12.* 32 | testbench: 10.* 33 | exclude: 34 | - laravel: 12.* 35 | php: 8.1 36 | - laravel: 12.* 37 | php: 8.0 38 | - laravel: 11.* 39 | php: 8.1 40 | - laravel: 11.* 41 | php: 8.0 42 | - laravel: 10.* 43 | php: 8.4 44 | - laravel: 10.* 45 | php: 8.0 46 | - laravel: 9.* 47 | php: 8.4 48 | - laravel: 9.* 49 | php: 8.3 50 | - laravel: 8.* 51 | php: 8.4 52 | - laravel: 8.* 53 | php: 8.3 54 | - laravel: 8.* 55 | php: 8.2 56 | 57 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} 58 | 59 | steps: 60 | - name: Checkout code 61 | uses: actions/checkout@v4 62 | 63 | - name: Setup PHP 64 | uses: shivammathur/setup-php@v2 65 | with: 66 | php-version: ${{ matrix.php }} 67 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 68 | coverage: none 69 | 70 | - name: Setup problem matchers 71 | run: | 72 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 73 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 74 | 75 | - name: Install dependencies 76 | run: | 77 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 78 | composer update --${{ matrix.stability }} --prefer-dist --no-interaction 79 | 80 | - name: Execute tests 81 | run: vendor/bin/pest 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .php_cs 3 | .php_cs.cache 4 | .phpunit.result.cache 5 | build 6 | composer.lock 7 | coverage 8 | docs 9 | phpunit.xml 10 | phpstan.neon 11 | testbench.yaml 12 | vendor 13 | node_modules 14 | .php-cs-fixer.cache -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Creagia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel pad signature 2 | 3 | A Laravel package to sign documents and optionally generate 4 | [certified PDFs](https://www.prepressure.com/pdf/basics/certified-pdf#:~:text=A%20Certified%20PDF%20is%20a,errors%20or%20notifications%20were%20generated) associated to a Eloquent model. 5 | 6 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/creagia/laravel-sign-pad.svg?style=flat-square)](https://packagist.org/packages/creagia/laravel-sign-pad) 7 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/creagia/laravel-sign-pad/run-tests.yml?label=tests)](https://github.com/creagia/laravel-sign-pad/actions?query=workflow%3Arun-tests+branch%3Amain) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/creagia/laravel-sign-pad.svg?style=flat-square)](https://packagist.org/packages/creagia/laravel-sign-pad) 9 | 10 | ## Requirements 11 | 12 | Laravel pad signature requires **PHP 8.0 - 8.4** and **Laravel 8 - 12**. 13 | 14 | ## Installation 15 | 16 | You can install the package via composer: 17 | 18 | ```bash 19 | composer require creagia/laravel-sign-pad 20 | ``` 21 | 22 | Publish the config and the migration files and migrate the database 23 | 24 | ```bash 25 | php artisan sign-pad:install 26 | ``` 27 | 28 | Publish the .js assets: 29 | 30 | ```bash 31 | php artisan vendor:publish --tag=sign-pad-assets 32 | ``` 33 | 34 | This will copy the package assets inside the `public/vendor/sign-pad/` folder. 35 | 36 | ## Configuration 37 | 38 | In the published config file `config/sign-pad.php` you'll be able to configure many important aspects of the package, 39 | like the route name where users will be redirected after signing the document or where do you want to store the signed documents. 40 | You can customize the disk and route to store signatures and documents. 41 | 42 | Notice that the redirect_route_name will receive the parameter `$uuid` with the uuid of the signature model in the database. 43 | 44 | ## Preparing your model 45 | 46 | Add the `RequiresSignature` trait and implement the `CanBeSigned` class to the model you would like. 47 | 48 | ```php 49 | 63 | ``` 64 | 65 | If you want to generate PDF documents with the signature, you should implement the `ShouldGenerateSignatureDocument` class . Define your document template with the `getSignatureDocumentTemplate` method. 66 | 67 | ```php 68 | 107 | ``` 108 | 109 | A `$model` object will be automatically injected into the Blade template, so you will be able to access all the needed properties of the model. 110 | 111 | ## Usage 112 | 113 | At this point, all you need is to create the form with the sign pad canvas in your template. For the route of the form, you have to call the method getSignatureRoute() from the instance of the model you prepared before: 114 | 115 | ```html 116 | @if (!$myModel->hasBeenSigned()) 117 |
118 | @csrf 119 |
120 | 121 |
122 |
123 | 124 | @endif 125 | ``` 126 | 127 | ### Retrieving signatures 128 | 129 | You can retrieve your model signature using the Eloquent relation `$myModel->signature`. After that, 130 | you can use: 131 | - `getSignatureImagePath()` returns the signature image path. 132 | - `getSignatureImageAbsolutePath()` returns the signature image absolute path. 133 | - `getSignedDocumentPath()` returns the generated PDF document path. 134 | - `getSignedDocumentAbsolutePath()` returns the generated PDF document absolute path. 135 | 136 | ```php 137 | echo $myModel->signature->getSignatureImagePath(); 138 | echo $myModel->signature->getSignedDocumentPath(); 139 | ``` 140 | 141 | ### Deleting signatures 142 | 143 | You can delete your model signature using 144 | - `deleteSignature()` method in the model. 145 | ```php 146 | echo $myModel->deleteSignature(); 147 | ``` 148 | 149 | ## Customizing the component 150 | 151 | From the same template, you can change the look of the component by passing some properties: 152 | - *border-color* (hex) to change the border color of the canvas 153 | - *pad-classes* and *button-classes* (strings) indicates which classes will have the sign area or the submit & clear buttons 154 | - *clear-name* and *submit-name* (strings) allows you to modify de default "Submit" and "Clear" values of the buttons. 155 | - *disabled-without-signature* (boolean) indicates if the submit button should be disabled when the user has not signed yet. 156 | 157 | An example with an app using Tailwind would be: 158 | 159 | ```html 160 | 168 | ``` 169 | 170 | ## Certifying the PDFs 171 | 172 | To certify your signature with TCPDF, you will have to create your own SSL certificate with OpenSSL. Otherwise you can 173 | find the TCPDF demo certificate 174 | here : [TCPDF Demo Certificate](https://github.com/tecnickcom/TCPDF/blob/main/examples/data/cert/tcpdf.crt) 175 | 176 | To create your own certificate use this command : 177 | 178 | ``` 179 | cd storage/app 180 | openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout certificate.crt -out certificate.crt 181 | ``` 182 | 183 | More information in the [TCPDF documentation](https://tcpdf.org/examples/example_052/) 184 | 185 | After generating the certificate, you'll have to change the value of the variable `certify_documents` in the `config/sign-pad.php` file and set it to **true**. 186 | 187 | When the variable `certify_documents` is set to true, the package will search the file allocated in the `certificate_file` path to sign the documents. Feel free to modify the location or the name of certificate file by changing its value. 188 | 189 | Inside the same `config/sign-pad.php` we encourage you to fill all the fields of the array `certificate_info` to be more specific with the certificate. 190 | 191 | Finally, you can change the certificate type by modifying the value of the variable `cert_type` (by default 2). You can find more information about certificates types at [TCPDF setSignature reference](https://hooks.wbcomdesigns.com/reference/classes/tcpdf/setsignature/). 192 | 193 | ## Upgrading 194 | 195 | See [UPGRADING](UPGRADING.md) for details. 196 | 197 | ## License 198 | 199 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 200 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrading 2 | ## From v1.x to v2.x 3 | - Delete the published Javascript assets from `public/vendor/sign-pad` and publish them again: 4 | 5 | ```php artisan vendor:publish --tag=sign-pad-assets``` 6 | 7 | - Add new config values and rename `store_path` to `signatures_path`: 8 | ```php 9 | /** 10 | * The disk on which to store signature images. Choose one or more of 11 | * the disks you've configured in config/filesystems.php. 12 | */ 13 | 'disk_name' => env('SIGNATURES_DISK', 'local'), 14 | 15 | /** 16 | * Path where the signature images will be stored. 17 | */ 18 | 'signatures_path' => 'signatures', 19 | 20 | /** 21 | * Path where the documents will be stored. 22 | */ 23 | 'documents_path' => 'signed_documents', 24 | ``` 25 | - Change the `SignatureDocumentTemplate` parameters to the new multiple signature positions array. For example replace: 26 | ```php 27 | use Creagia\LaravelSignPad\SignatureDocumentTemplate; 28 | use Creagia\LaravelSignPad\Templates\PdfDocumentTemplate; 29 | 30 | public function getSignatureDocumentTemplate(): SignatureDocumentTemplate 31 | { 32 | return new SignatureDocumentTemplate( 33 | signaturePage: 1, 34 | signatureX: 20, 35 | signatureY: 25, 36 | outputPdfPrefix: 'document', // optional 37 | template: new PdfDocumentTemplate(storage_path('pdf/template.pdf')), 38 | ); 39 | } 40 | ``` 41 | With: 42 | ```php 43 | use Creagia\LaravelSignPad\Templates\PdfDocumentTemplate; 44 | use Creagia\LaravelSignPad\SignatureDocumentTemplate; 45 | use Creagia\LaravelSignPad\SignaturePosition; 46 | 47 | public function getSignatureDocumentTemplate(): SignatureDocumentTemplate 48 | { 49 | return new SignatureDocumentTemplate( 50 | outputPdfPrefix: 'document', // optional 51 | template: new PdfDocumentTemplate(storage_path('pdf/template.pdf')), // Uncomment for PDF template 52 | signaturePositions: [ 53 | new SignaturePosition( 54 | signaturePage: 1, 55 | signatureX: 20, 56 | signatureY: 25, 57 | ), 58 | new SignaturePosition( 59 | signaturePage: 2, 60 | signatureX: 25, 61 | signatureY: 50, 62 | ), 63 | ] 64 | ); 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "creagia/laravel-sign-pad", 3 | "description": "Laravel package for of E-Signature with Signature Pad and Digital Certified Sign with TCPDF", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "creagia", 8 | "email": "info@creagia.com" 9 | } 10 | ], 11 | "require": { 12 | "php": "^8.0", 13 | "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0", 14 | "spatie/laravel-package-tools": "^1.12.1", 15 | "setasign/fpdi": "^v2.0.0", 16 | "tecnickcom/tcpdf": "^6.6.0" 17 | }, 18 | "require-dev": { 19 | "laravel/pint": "^1.0", 20 | "nunomaduro/collision": "^5.10|^6.0|^8.0", 21 | "nunomaduro/larastan": "^1.0|^2.0|^3.0", 22 | "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0", 23 | "pestphp/pest": "^1.21|^2.34|^3.7", 24 | "pestphp/pest-plugin-laravel": "^1.1|^2.3|^3.1", 25 | "phpstan/extension-installer": "^1.1", 26 | "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", 27 | "phpstan/phpstan-phpunit": "^1.0|^2.0", 28 | "phpunit/phpunit": "^9.5|^10.5|^11.5.3" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "Creagia\\LaravelSignPad\\": "src" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "Creagia\\LaravelSignPad\\Tests\\": "tests" 38 | } 39 | }, 40 | "extra": { 41 | "laravel": { 42 | "providers": [ 43 | "Creagia\\LaravelSignPad\\LaravelSignPadServiceProvider" 44 | ] 45 | } 46 | }, 47 | "scripts": { 48 | "analyse": "vendor/bin/phpstan analyse", 49 | "test": "vendor/bin/pest", 50 | "test-coverage": "vendor/bin/pest coverage", 51 | "format": "vendor/bin/php-cs-fixer fix --allow-risky=yes" 52 | }, 53 | "config": { 54 | "sort-packages": true, 55 | "allow-plugins": { 56 | "pestphp/pest-plugin": true, 57 | "phpstan/extension-installer": true 58 | } 59 | }, 60 | "minimum-stability": "dev", 61 | "prefer-stable": true 62 | } 63 | -------------------------------------------------------------------------------- /config/sign-pad.php: -------------------------------------------------------------------------------- 1 | env('SIGNATURES_DISK', 'local'), 9 | 10 | /** 11 | * Path where the signature images will be stored. 12 | */ 13 | 'signatures_path' => 'signatures', 14 | 15 | /** 16 | * Path where the documents will be stored. 17 | */ 18 | 'documents_path' => 'signed_documents', 19 | 20 | /** 21 | * Route name where you want to redirect users after signing. 22 | */ 23 | 'redirect_route_name' => null, 24 | 25 | /** 26 | * Width and height of the signature rectangle. 27 | */ 28 | 'width' => 600, 29 | 'height' => 200, 30 | 31 | /** 32 | * Should certify the document signature with certificate 33 | */ 34 | 'certify_documents' => false, 35 | 36 | /** 37 | * Certificate path 38 | */ 39 | 'certificate_file' => storage_path('app/certificate.crt'), 40 | 41 | /** 42 | * Signature certificate information. Will be attached to the generated signature in the PDF file. 43 | */ 44 | 'certificate_info' => [ 45 | 'Name' => '', 46 | 'Location' => '', 47 | 'Reason' => '', 48 | 'ContactInfo' => '', 49 | ], 50 | 51 | /** 52 | * Access permissions granted for this document. 53 | * More information: https://hooks.wbcomdesigns.com/reference/classes/tcpdf/setsignature/#parameters 54 | */ 55 | 'cert_type' => 2, 56 | ]; 57 | -------------------------------------------------------------------------------- /database/migrations/create_signatures_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->morphs('model'); 19 | $table->string('uuid'); 20 | $table->string('filename'); 21 | $table->string('document_filename')->nullable(); 22 | $table->boolean('certified')->default(false); 23 | $table->json('from_ips')->nullable(); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('signatures'); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-sign-pad", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "laravel-sign-pad", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "signature_pad": "^4.0.0" 13 | }, 14 | "devDependencies": { 15 | "webpack": "^5.74.0", 16 | "webpack-cli": "^4.10.0" 17 | } 18 | }, 19 | "node_modules/@discoveryjs/json-ext": { 20 | "version": "0.5.7", 21 | "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", 22 | "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", 23 | "dev": true, 24 | "engines": { 25 | "node": ">=10.0.0" 26 | } 27 | }, 28 | "node_modules/@jridgewell/gen-mapping": { 29 | "version": "0.3.2", 30 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 31 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 32 | "dev": true, 33 | "dependencies": { 34 | "@jridgewell/set-array": "^1.0.1", 35 | "@jridgewell/sourcemap-codec": "^1.4.10", 36 | "@jridgewell/trace-mapping": "^0.3.9" 37 | }, 38 | "engines": { 39 | "node": ">=6.0.0" 40 | } 41 | }, 42 | "node_modules/@jridgewell/resolve-uri": { 43 | "version": "3.1.0", 44 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 45 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 46 | "dev": true, 47 | "engines": { 48 | "node": ">=6.0.0" 49 | } 50 | }, 51 | "node_modules/@jridgewell/set-array": { 52 | "version": "1.1.2", 53 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 54 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 55 | "dev": true, 56 | "engines": { 57 | "node": ">=6.0.0" 58 | } 59 | }, 60 | "node_modules/@jridgewell/source-map": { 61 | "version": "0.3.2", 62 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", 63 | "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", 64 | "dev": true, 65 | "dependencies": { 66 | "@jridgewell/gen-mapping": "^0.3.0", 67 | "@jridgewell/trace-mapping": "^0.3.9" 68 | } 69 | }, 70 | "node_modules/@jridgewell/sourcemap-codec": { 71 | "version": "1.4.14", 72 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 73 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 74 | "dev": true 75 | }, 76 | "node_modules/@jridgewell/trace-mapping": { 77 | "version": "0.3.15", 78 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", 79 | "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", 80 | "dev": true, 81 | "dependencies": { 82 | "@jridgewell/resolve-uri": "^3.0.3", 83 | "@jridgewell/sourcemap-codec": "^1.4.10" 84 | } 85 | }, 86 | "node_modules/@types/eslint": { 87 | "version": "8.4.6", 88 | "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", 89 | "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", 90 | "dev": true, 91 | "dependencies": { 92 | "@types/estree": "*", 93 | "@types/json-schema": "*" 94 | } 95 | }, 96 | "node_modules/@types/eslint-scope": { 97 | "version": "3.7.4", 98 | "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", 99 | "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", 100 | "dev": true, 101 | "dependencies": { 102 | "@types/eslint": "*", 103 | "@types/estree": "*" 104 | } 105 | }, 106 | "node_modules/@types/estree": { 107 | "version": "0.0.51", 108 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", 109 | "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", 110 | "dev": true 111 | }, 112 | "node_modules/@types/json-schema": { 113 | "version": "7.0.11", 114 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", 115 | "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", 116 | "dev": true 117 | }, 118 | "node_modules/@types/node": { 119 | "version": "18.7.18", 120 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", 121 | "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", 122 | "dev": true 123 | }, 124 | "node_modules/@webassemblyjs/ast": { 125 | "version": "1.11.1", 126 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", 127 | "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", 128 | "dev": true, 129 | "dependencies": { 130 | "@webassemblyjs/helper-numbers": "1.11.1", 131 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1" 132 | } 133 | }, 134 | "node_modules/@webassemblyjs/floating-point-hex-parser": { 135 | "version": "1.11.1", 136 | "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", 137 | "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", 138 | "dev": true 139 | }, 140 | "node_modules/@webassemblyjs/helper-api-error": { 141 | "version": "1.11.1", 142 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", 143 | "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", 144 | "dev": true 145 | }, 146 | "node_modules/@webassemblyjs/helper-buffer": { 147 | "version": "1.11.1", 148 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", 149 | "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", 150 | "dev": true 151 | }, 152 | "node_modules/@webassemblyjs/helper-numbers": { 153 | "version": "1.11.1", 154 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", 155 | "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", 156 | "dev": true, 157 | "dependencies": { 158 | "@webassemblyjs/floating-point-hex-parser": "1.11.1", 159 | "@webassemblyjs/helper-api-error": "1.11.1", 160 | "@xtuc/long": "4.2.2" 161 | } 162 | }, 163 | "node_modules/@webassemblyjs/helper-wasm-bytecode": { 164 | "version": "1.11.1", 165 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", 166 | "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", 167 | "dev": true 168 | }, 169 | "node_modules/@webassemblyjs/helper-wasm-section": { 170 | "version": "1.11.1", 171 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", 172 | "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", 173 | "dev": true, 174 | "dependencies": { 175 | "@webassemblyjs/ast": "1.11.1", 176 | "@webassemblyjs/helper-buffer": "1.11.1", 177 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 178 | "@webassemblyjs/wasm-gen": "1.11.1" 179 | } 180 | }, 181 | "node_modules/@webassemblyjs/ieee754": { 182 | "version": "1.11.1", 183 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", 184 | "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", 185 | "dev": true, 186 | "dependencies": { 187 | "@xtuc/ieee754": "^1.2.0" 188 | } 189 | }, 190 | "node_modules/@webassemblyjs/leb128": { 191 | "version": "1.11.1", 192 | "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", 193 | "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", 194 | "dev": true, 195 | "dependencies": { 196 | "@xtuc/long": "4.2.2" 197 | } 198 | }, 199 | "node_modules/@webassemblyjs/utf8": { 200 | "version": "1.11.1", 201 | "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", 202 | "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", 203 | "dev": true 204 | }, 205 | "node_modules/@webassemblyjs/wasm-edit": { 206 | "version": "1.11.1", 207 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", 208 | "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", 209 | "dev": true, 210 | "dependencies": { 211 | "@webassemblyjs/ast": "1.11.1", 212 | "@webassemblyjs/helper-buffer": "1.11.1", 213 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 214 | "@webassemblyjs/helper-wasm-section": "1.11.1", 215 | "@webassemblyjs/wasm-gen": "1.11.1", 216 | "@webassemblyjs/wasm-opt": "1.11.1", 217 | "@webassemblyjs/wasm-parser": "1.11.1", 218 | "@webassemblyjs/wast-printer": "1.11.1" 219 | } 220 | }, 221 | "node_modules/@webassemblyjs/wasm-gen": { 222 | "version": "1.11.1", 223 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", 224 | "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", 225 | "dev": true, 226 | "dependencies": { 227 | "@webassemblyjs/ast": "1.11.1", 228 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 229 | "@webassemblyjs/ieee754": "1.11.1", 230 | "@webassemblyjs/leb128": "1.11.1", 231 | "@webassemblyjs/utf8": "1.11.1" 232 | } 233 | }, 234 | "node_modules/@webassemblyjs/wasm-opt": { 235 | "version": "1.11.1", 236 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", 237 | "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", 238 | "dev": true, 239 | "dependencies": { 240 | "@webassemblyjs/ast": "1.11.1", 241 | "@webassemblyjs/helper-buffer": "1.11.1", 242 | "@webassemblyjs/wasm-gen": "1.11.1", 243 | "@webassemblyjs/wasm-parser": "1.11.1" 244 | } 245 | }, 246 | "node_modules/@webassemblyjs/wasm-parser": { 247 | "version": "1.11.1", 248 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", 249 | "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", 250 | "dev": true, 251 | "dependencies": { 252 | "@webassemblyjs/ast": "1.11.1", 253 | "@webassemblyjs/helper-api-error": "1.11.1", 254 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 255 | "@webassemblyjs/ieee754": "1.11.1", 256 | "@webassemblyjs/leb128": "1.11.1", 257 | "@webassemblyjs/utf8": "1.11.1" 258 | } 259 | }, 260 | "node_modules/@webassemblyjs/wast-printer": { 261 | "version": "1.11.1", 262 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", 263 | "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", 264 | "dev": true, 265 | "dependencies": { 266 | "@webassemblyjs/ast": "1.11.1", 267 | "@xtuc/long": "4.2.2" 268 | } 269 | }, 270 | "node_modules/@webpack-cli/configtest": { 271 | "version": "1.2.0", 272 | "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", 273 | "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", 274 | "dev": true, 275 | "peerDependencies": { 276 | "webpack": "4.x.x || 5.x.x", 277 | "webpack-cli": "4.x.x" 278 | } 279 | }, 280 | "node_modules/@webpack-cli/info": { 281 | "version": "1.5.0", 282 | "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", 283 | "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", 284 | "dev": true, 285 | "dependencies": { 286 | "envinfo": "^7.7.3" 287 | }, 288 | "peerDependencies": { 289 | "webpack-cli": "4.x.x" 290 | } 291 | }, 292 | "node_modules/@webpack-cli/serve": { 293 | "version": "1.7.0", 294 | "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", 295 | "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", 296 | "dev": true, 297 | "peerDependencies": { 298 | "webpack-cli": "4.x.x" 299 | }, 300 | "peerDependenciesMeta": { 301 | "webpack-dev-server": { 302 | "optional": true 303 | } 304 | } 305 | }, 306 | "node_modules/@xtuc/ieee754": { 307 | "version": "1.2.0", 308 | "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", 309 | "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", 310 | "dev": true 311 | }, 312 | "node_modules/@xtuc/long": { 313 | "version": "4.2.2", 314 | "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", 315 | "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", 316 | "dev": true 317 | }, 318 | "node_modules/acorn": { 319 | "version": "8.8.0", 320 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", 321 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", 322 | "dev": true, 323 | "bin": { 324 | "acorn": "bin/acorn" 325 | }, 326 | "engines": { 327 | "node": ">=0.4.0" 328 | } 329 | }, 330 | "node_modules/acorn-import-assertions": { 331 | "version": "1.8.0", 332 | "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", 333 | "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", 334 | "dev": true, 335 | "peerDependencies": { 336 | "acorn": "^8" 337 | } 338 | }, 339 | "node_modules/ajv": { 340 | "version": "6.12.6", 341 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 342 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 343 | "dev": true, 344 | "dependencies": { 345 | "fast-deep-equal": "^3.1.1", 346 | "fast-json-stable-stringify": "^2.0.0", 347 | "json-schema-traverse": "^0.4.1", 348 | "uri-js": "^4.2.2" 349 | }, 350 | "funding": { 351 | "type": "github", 352 | "url": "https://github.com/sponsors/epoberezkin" 353 | } 354 | }, 355 | "node_modules/ajv-keywords": { 356 | "version": "3.5.2", 357 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", 358 | "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", 359 | "dev": true, 360 | "peerDependencies": { 361 | "ajv": "^6.9.1" 362 | } 363 | }, 364 | "node_modules/browserslist": { 365 | "version": "4.21.4", 366 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", 367 | "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", 368 | "dev": true, 369 | "funding": [ 370 | { 371 | "type": "opencollective", 372 | "url": "https://opencollective.com/browserslist" 373 | }, 374 | { 375 | "type": "tidelift", 376 | "url": "https://tidelift.com/funding/github/npm/browserslist" 377 | } 378 | ], 379 | "dependencies": { 380 | "caniuse-lite": "^1.0.30001400", 381 | "electron-to-chromium": "^1.4.251", 382 | "node-releases": "^2.0.6", 383 | "update-browserslist-db": "^1.0.9" 384 | }, 385 | "bin": { 386 | "browserslist": "cli.js" 387 | }, 388 | "engines": { 389 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 390 | } 391 | }, 392 | "node_modules/buffer-from": { 393 | "version": "1.1.2", 394 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 395 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 396 | "dev": true 397 | }, 398 | "node_modules/caniuse-lite": { 399 | "version": "1.0.30001409", 400 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", 401 | "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", 402 | "dev": true, 403 | "funding": [ 404 | { 405 | "type": "opencollective", 406 | "url": "https://opencollective.com/browserslist" 407 | }, 408 | { 409 | "type": "tidelift", 410 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 411 | } 412 | ] 413 | }, 414 | "node_modules/chrome-trace-event": { 415 | "version": "1.0.3", 416 | "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", 417 | "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", 418 | "dev": true, 419 | "engines": { 420 | "node": ">=6.0" 421 | } 422 | }, 423 | "node_modules/clone-deep": { 424 | "version": "4.0.1", 425 | "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", 426 | "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", 427 | "dev": true, 428 | "dependencies": { 429 | "is-plain-object": "^2.0.4", 430 | "kind-of": "^6.0.2", 431 | "shallow-clone": "^3.0.0" 432 | }, 433 | "engines": { 434 | "node": ">=6" 435 | } 436 | }, 437 | "node_modules/colorette": { 438 | "version": "2.0.19", 439 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", 440 | "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", 441 | "dev": true 442 | }, 443 | "node_modules/commander": { 444 | "version": "2.20.3", 445 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 446 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 447 | "dev": true 448 | }, 449 | "node_modules/cross-spawn": { 450 | "version": "7.0.3", 451 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 452 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 453 | "dev": true, 454 | "dependencies": { 455 | "path-key": "^3.1.0", 456 | "shebang-command": "^2.0.0", 457 | "which": "^2.0.1" 458 | }, 459 | "engines": { 460 | "node": ">= 8" 461 | } 462 | }, 463 | "node_modules/electron-to-chromium": { 464 | "version": "1.4.256", 465 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", 466 | "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", 467 | "dev": true 468 | }, 469 | "node_modules/enhanced-resolve": { 470 | "version": "5.10.0", 471 | "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", 472 | "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", 473 | "dev": true, 474 | "dependencies": { 475 | "graceful-fs": "^4.2.4", 476 | "tapable": "^2.2.0" 477 | }, 478 | "engines": { 479 | "node": ">=10.13.0" 480 | } 481 | }, 482 | "node_modules/envinfo": { 483 | "version": "7.8.1", 484 | "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", 485 | "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", 486 | "dev": true, 487 | "bin": { 488 | "envinfo": "dist/cli.js" 489 | }, 490 | "engines": { 491 | "node": ">=4" 492 | } 493 | }, 494 | "node_modules/es-module-lexer": { 495 | "version": "0.9.3", 496 | "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", 497 | "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", 498 | "dev": true 499 | }, 500 | "node_modules/escalade": { 501 | "version": "3.1.1", 502 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 503 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 504 | "dev": true, 505 | "engines": { 506 | "node": ">=6" 507 | } 508 | }, 509 | "node_modules/eslint-scope": { 510 | "version": "5.1.1", 511 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 512 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 513 | "dev": true, 514 | "dependencies": { 515 | "esrecurse": "^4.3.0", 516 | "estraverse": "^4.1.1" 517 | }, 518 | "engines": { 519 | "node": ">=8.0.0" 520 | } 521 | }, 522 | "node_modules/esrecurse": { 523 | "version": "4.3.0", 524 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 525 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 526 | "dev": true, 527 | "dependencies": { 528 | "estraverse": "^5.2.0" 529 | }, 530 | "engines": { 531 | "node": ">=4.0" 532 | } 533 | }, 534 | "node_modules/esrecurse/node_modules/estraverse": { 535 | "version": "5.3.0", 536 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 537 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 538 | "dev": true, 539 | "engines": { 540 | "node": ">=4.0" 541 | } 542 | }, 543 | "node_modules/estraverse": { 544 | "version": "4.3.0", 545 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 546 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 547 | "dev": true, 548 | "engines": { 549 | "node": ">=4.0" 550 | } 551 | }, 552 | "node_modules/events": { 553 | "version": "3.3.0", 554 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 555 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 556 | "dev": true, 557 | "engines": { 558 | "node": ">=0.8.x" 559 | } 560 | }, 561 | "node_modules/fast-deep-equal": { 562 | "version": "3.1.3", 563 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 564 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 565 | "dev": true 566 | }, 567 | "node_modules/fast-json-stable-stringify": { 568 | "version": "2.1.0", 569 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 570 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 571 | "dev": true 572 | }, 573 | "node_modules/fastest-levenshtein": { 574 | "version": "1.0.16", 575 | "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", 576 | "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", 577 | "dev": true, 578 | "engines": { 579 | "node": ">= 4.9.1" 580 | } 581 | }, 582 | "node_modules/find-up": { 583 | "version": "4.1.0", 584 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 585 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 586 | "dev": true, 587 | "dependencies": { 588 | "locate-path": "^5.0.0", 589 | "path-exists": "^4.0.0" 590 | }, 591 | "engines": { 592 | "node": ">=8" 593 | } 594 | }, 595 | "node_modules/function-bind": { 596 | "version": "1.1.1", 597 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 598 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 599 | "dev": true 600 | }, 601 | "node_modules/glob-to-regexp": { 602 | "version": "0.4.1", 603 | "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 604 | "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 605 | "dev": true 606 | }, 607 | "node_modules/graceful-fs": { 608 | "version": "4.2.10", 609 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 610 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", 611 | "dev": true 612 | }, 613 | "node_modules/has": { 614 | "version": "1.0.3", 615 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 616 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 617 | "dev": true, 618 | "dependencies": { 619 | "function-bind": "^1.1.1" 620 | }, 621 | "engines": { 622 | "node": ">= 0.4.0" 623 | } 624 | }, 625 | "node_modules/has-flag": { 626 | "version": "4.0.0", 627 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 628 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 629 | "dev": true, 630 | "engines": { 631 | "node": ">=8" 632 | } 633 | }, 634 | "node_modules/import-local": { 635 | "version": "3.1.0", 636 | "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", 637 | "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", 638 | "dev": true, 639 | "dependencies": { 640 | "pkg-dir": "^4.2.0", 641 | "resolve-cwd": "^3.0.0" 642 | }, 643 | "bin": { 644 | "import-local-fixture": "fixtures/cli.js" 645 | }, 646 | "engines": { 647 | "node": ">=8" 648 | }, 649 | "funding": { 650 | "url": "https://github.com/sponsors/sindresorhus" 651 | } 652 | }, 653 | "node_modules/interpret": { 654 | "version": "2.2.0", 655 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", 656 | "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", 657 | "dev": true, 658 | "engines": { 659 | "node": ">= 0.10" 660 | } 661 | }, 662 | "node_modules/is-core-module": { 663 | "version": "2.10.0", 664 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", 665 | "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", 666 | "dev": true, 667 | "dependencies": { 668 | "has": "^1.0.3" 669 | }, 670 | "funding": { 671 | "url": "https://github.com/sponsors/ljharb" 672 | } 673 | }, 674 | "node_modules/is-plain-object": { 675 | "version": "2.0.4", 676 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 677 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", 678 | "dev": true, 679 | "dependencies": { 680 | "isobject": "^3.0.1" 681 | }, 682 | "engines": { 683 | "node": ">=0.10.0" 684 | } 685 | }, 686 | "node_modules/isexe": { 687 | "version": "2.0.0", 688 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 689 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 690 | "dev": true 691 | }, 692 | "node_modules/isobject": { 693 | "version": "3.0.1", 694 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 695 | "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", 696 | "dev": true, 697 | "engines": { 698 | "node": ">=0.10.0" 699 | } 700 | }, 701 | "node_modules/jest-worker": { 702 | "version": "27.5.1", 703 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", 704 | "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", 705 | "dev": true, 706 | "dependencies": { 707 | "@types/node": "*", 708 | "merge-stream": "^2.0.0", 709 | "supports-color": "^8.0.0" 710 | }, 711 | "engines": { 712 | "node": ">= 10.13.0" 713 | } 714 | }, 715 | "node_modules/json-parse-even-better-errors": { 716 | "version": "2.3.1", 717 | "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", 718 | "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", 719 | "dev": true 720 | }, 721 | "node_modules/json-schema-traverse": { 722 | "version": "0.4.1", 723 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 724 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 725 | "dev": true 726 | }, 727 | "node_modules/kind-of": { 728 | "version": "6.0.3", 729 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 730 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", 731 | "dev": true, 732 | "engines": { 733 | "node": ">=0.10.0" 734 | } 735 | }, 736 | "node_modules/loader-runner": { 737 | "version": "4.3.0", 738 | "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", 739 | "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", 740 | "dev": true, 741 | "engines": { 742 | "node": ">=6.11.5" 743 | } 744 | }, 745 | "node_modules/locate-path": { 746 | "version": "5.0.0", 747 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 748 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 749 | "dev": true, 750 | "dependencies": { 751 | "p-locate": "^4.1.0" 752 | }, 753 | "engines": { 754 | "node": ">=8" 755 | } 756 | }, 757 | "node_modules/merge-stream": { 758 | "version": "2.0.0", 759 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 760 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 761 | "dev": true 762 | }, 763 | "node_modules/mime-db": { 764 | "version": "1.52.0", 765 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 766 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 767 | "dev": true, 768 | "engines": { 769 | "node": ">= 0.6" 770 | } 771 | }, 772 | "node_modules/mime-types": { 773 | "version": "2.1.35", 774 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 775 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 776 | "dev": true, 777 | "dependencies": { 778 | "mime-db": "1.52.0" 779 | }, 780 | "engines": { 781 | "node": ">= 0.6" 782 | } 783 | }, 784 | "node_modules/neo-async": { 785 | "version": "2.6.2", 786 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", 787 | "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", 788 | "dev": true 789 | }, 790 | "node_modules/node-releases": { 791 | "version": "2.0.6", 792 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", 793 | "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", 794 | "dev": true 795 | }, 796 | "node_modules/p-limit": { 797 | "version": "2.3.0", 798 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 799 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 800 | "dev": true, 801 | "dependencies": { 802 | "p-try": "^2.0.0" 803 | }, 804 | "engines": { 805 | "node": ">=6" 806 | }, 807 | "funding": { 808 | "url": "https://github.com/sponsors/sindresorhus" 809 | } 810 | }, 811 | "node_modules/p-locate": { 812 | "version": "4.1.0", 813 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 814 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 815 | "dev": true, 816 | "dependencies": { 817 | "p-limit": "^2.2.0" 818 | }, 819 | "engines": { 820 | "node": ">=8" 821 | } 822 | }, 823 | "node_modules/p-try": { 824 | "version": "2.2.0", 825 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 826 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 827 | "dev": true, 828 | "engines": { 829 | "node": ">=6" 830 | } 831 | }, 832 | "node_modules/path-exists": { 833 | "version": "4.0.0", 834 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 835 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 836 | "dev": true, 837 | "engines": { 838 | "node": ">=8" 839 | } 840 | }, 841 | "node_modules/path-key": { 842 | "version": "3.1.1", 843 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 844 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 845 | "dev": true, 846 | "engines": { 847 | "node": ">=8" 848 | } 849 | }, 850 | "node_modules/path-parse": { 851 | "version": "1.0.7", 852 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 853 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 854 | "dev": true 855 | }, 856 | "node_modules/picocolors": { 857 | "version": "1.0.0", 858 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 859 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 860 | "dev": true 861 | }, 862 | "node_modules/pkg-dir": { 863 | "version": "4.2.0", 864 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 865 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 866 | "dev": true, 867 | "dependencies": { 868 | "find-up": "^4.0.0" 869 | }, 870 | "engines": { 871 | "node": ">=8" 872 | } 873 | }, 874 | "node_modules/punycode": { 875 | "version": "2.1.1", 876 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 877 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 878 | "dev": true, 879 | "engines": { 880 | "node": ">=6" 881 | } 882 | }, 883 | "node_modules/randombytes": { 884 | "version": "2.1.0", 885 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 886 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 887 | "dev": true, 888 | "dependencies": { 889 | "safe-buffer": "^5.1.0" 890 | } 891 | }, 892 | "node_modules/rechoir": { 893 | "version": "0.7.1", 894 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", 895 | "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", 896 | "dev": true, 897 | "dependencies": { 898 | "resolve": "^1.9.0" 899 | }, 900 | "engines": { 901 | "node": ">= 0.10" 902 | } 903 | }, 904 | "node_modules/resolve": { 905 | "version": "1.22.1", 906 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 907 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 908 | "dev": true, 909 | "dependencies": { 910 | "is-core-module": "^2.9.0", 911 | "path-parse": "^1.0.7", 912 | "supports-preserve-symlinks-flag": "^1.0.0" 913 | }, 914 | "bin": { 915 | "resolve": "bin/resolve" 916 | }, 917 | "funding": { 918 | "url": "https://github.com/sponsors/ljharb" 919 | } 920 | }, 921 | "node_modules/resolve-cwd": { 922 | "version": "3.0.0", 923 | "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", 924 | "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", 925 | "dev": true, 926 | "dependencies": { 927 | "resolve-from": "^5.0.0" 928 | }, 929 | "engines": { 930 | "node": ">=8" 931 | } 932 | }, 933 | "node_modules/resolve-from": { 934 | "version": "5.0.0", 935 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", 936 | "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", 937 | "dev": true, 938 | "engines": { 939 | "node": ">=8" 940 | } 941 | }, 942 | "node_modules/safe-buffer": { 943 | "version": "5.2.1", 944 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 945 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 946 | "dev": true, 947 | "funding": [ 948 | { 949 | "type": "github", 950 | "url": "https://github.com/sponsors/feross" 951 | }, 952 | { 953 | "type": "patreon", 954 | "url": "https://www.patreon.com/feross" 955 | }, 956 | { 957 | "type": "consulting", 958 | "url": "https://feross.org/support" 959 | } 960 | ] 961 | }, 962 | "node_modules/schema-utils": { 963 | "version": "3.1.1", 964 | "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", 965 | "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", 966 | "dev": true, 967 | "dependencies": { 968 | "@types/json-schema": "^7.0.8", 969 | "ajv": "^6.12.5", 970 | "ajv-keywords": "^3.5.2" 971 | }, 972 | "engines": { 973 | "node": ">= 10.13.0" 974 | }, 975 | "funding": { 976 | "type": "opencollective", 977 | "url": "https://opencollective.com/webpack" 978 | } 979 | }, 980 | "node_modules/serialize-javascript": { 981 | "version": "6.0.0", 982 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 983 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 984 | "dev": true, 985 | "dependencies": { 986 | "randombytes": "^2.1.0" 987 | } 988 | }, 989 | "node_modules/shallow-clone": { 990 | "version": "3.0.1", 991 | "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", 992 | "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", 993 | "dev": true, 994 | "dependencies": { 995 | "kind-of": "^6.0.2" 996 | }, 997 | "engines": { 998 | "node": ">=8" 999 | } 1000 | }, 1001 | "node_modules/shebang-command": { 1002 | "version": "2.0.0", 1003 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1004 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1005 | "dev": true, 1006 | "dependencies": { 1007 | "shebang-regex": "^3.0.0" 1008 | }, 1009 | "engines": { 1010 | "node": ">=8" 1011 | } 1012 | }, 1013 | "node_modules/shebang-regex": { 1014 | "version": "3.0.0", 1015 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1016 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1017 | "dev": true, 1018 | "engines": { 1019 | "node": ">=8" 1020 | } 1021 | }, 1022 | "node_modules/signature_pad": { 1023 | "version": "4.0.0", 1024 | "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-4.0.0.tgz", 1025 | "integrity": "sha512-47I2MULJIHaUAFuVbqMVWwZR+GEl2wH4QKg16OwpQg82GzVUL3Z405Um6sLgVB0xbP5rwqiNzfLTbJwipXvaWA==" 1026 | }, 1027 | "node_modules/source-map": { 1028 | "version": "0.6.1", 1029 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1030 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1031 | "dev": true, 1032 | "engines": { 1033 | "node": ">=0.10.0" 1034 | } 1035 | }, 1036 | "node_modules/source-map-support": { 1037 | "version": "0.5.21", 1038 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1039 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1040 | "dev": true, 1041 | "dependencies": { 1042 | "buffer-from": "^1.0.0", 1043 | "source-map": "^0.6.0" 1044 | } 1045 | }, 1046 | "node_modules/supports-color": { 1047 | "version": "8.1.1", 1048 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1049 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1050 | "dev": true, 1051 | "dependencies": { 1052 | "has-flag": "^4.0.0" 1053 | }, 1054 | "engines": { 1055 | "node": ">=10" 1056 | }, 1057 | "funding": { 1058 | "url": "https://github.com/chalk/supports-color?sponsor=1" 1059 | } 1060 | }, 1061 | "node_modules/supports-preserve-symlinks-flag": { 1062 | "version": "1.0.0", 1063 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1064 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1065 | "dev": true, 1066 | "engines": { 1067 | "node": ">= 0.4" 1068 | }, 1069 | "funding": { 1070 | "url": "https://github.com/sponsors/ljharb" 1071 | } 1072 | }, 1073 | "node_modules/tapable": { 1074 | "version": "2.2.1", 1075 | "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", 1076 | "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", 1077 | "dev": true, 1078 | "engines": { 1079 | "node": ">=6" 1080 | } 1081 | }, 1082 | "node_modules/terser": { 1083 | "version": "5.15.0", 1084 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", 1085 | "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", 1086 | "dev": true, 1087 | "dependencies": { 1088 | "@jridgewell/source-map": "^0.3.2", 1089 | "acorn": "^8.5.0", 1090 | "commander": "^2.20.0", 1091 | "source-map-support": "~0.5.20" 1092 | }, 1093 | "bin": { 1094 | "terser": "bin/terser" 1095 | }, 1096 | "engines": { 1097 | "node": ">=10" 1098 | } 1099 | }, 1100 | "node_modules/terser-webpack-plugin": { 1101 | "version": "5.3.6", 1102 | "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", 1103 | "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", 1104 | "dev": true, 1105 | "dependencies": { 1106 | "@jridgewell/trace-mapping": "^0.3.14", 1107 | "jest-worker": "^27.4.5", 1108 | "schema-utils": "^3.1.1", 1109 | "serialize-javascript": "^6.0.0", 1110 | "terser": "^5.14.1" 1111 | }, 1112 | "engines": { 1113 | "node": ">= 10.13.0" 1114 | }, 1115 | "funding": { 1116 | "type": "opencollective", 1117 | "url": "https://opencollective.com/webpack" 1118 | }, 1119 | "peerDependencies": { 1120 | "webpack": "^5.1.0" 1121 | }, 1122 | "peerDependenciesMeta": { 1123 | "@swc/core": { 1124 | "optional": true 1125 | }, 1126 | "esbuild": { 1127 | "optional": true 1128 | }, 1129 | "uglify-js": { 1130 | "optional": true 1131 | } 1132 | } 1133 | }, 1134 | "node_modules/update-browserslist-db": { 1135 | "version": "1.0.9", 1136 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", 1137 | "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", 1138 | "dev": true, 1139 | "funding": [ 1140 | { 1141 | "type": "opencollective", 1142 | "url": "https://opencollective.com/browserslist" 1143 | }, 1144 | { 1145 | "type": "tidelift", 1146 | "url": "https://tidelift.com/funding/github/npm/browserslist" 1147 | } 1148 | ], 1149 | "dependencies": { 1150 | "escalade": "^3.1.1", 1151 | "picocolors": "^1.0.0" 1152 | }, 1153 | "bin": { 1154 | "browserslist-lint": "cli.js" 1155 | }, 1156 | "peerDependencies": { 1157 | "browserslist": ">= 4.21.0" 1158 | } 1159 | }, 1160 | "node_modules/uri-js": { 1161 | "version": "4.4.1", 1162 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1163 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1164 | "dev": true, 1165 | "dependencies": { 1166 | "punycode": "^2.1.0" 1167 | } 1168 | }, 1169 | "node_modules/watchpack": { 1170 | "version": "2.4.0", 1171 | "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", 1172 | "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", 1173 | "dev": true, 1174 | "dependencies": { 1175 | "glob-to-regexp": "^0.4.1", 1176 | "graceful-fs": "^4.1.2" 1177 | }, 1178 | "engines": { 1179 | "node": ">=10.13.0" 1180 | } 1181 | }, 1182 | "node_modules/webpack": { 1183 | "version": "5.74.0", 1184 | "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", 1185 | "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", 1186 | "dev": true, 1187 | "dependencies": { 1188 | "@types/eslint-scope": "^3.7.3", 1189 | "@types/estree": "^0.0.51", 1190 | "@webassemblyjs/ast": "1.11.1", 1191 | "@webassemblyjs/wasm-edit": "1.11.1", 1192 | "@webassemblyjs/wasm-parser": "1.11.1", 1193 | "acorn": "^8.7.1", 1194 | "acorn-import-assertions": "^1.7.6", 1195 | "browserslist": "^4.14.5", 1196 | "chrome-trace-event": "^1.0.2", 1197 | "enhanced-resolve": "^5.10.0", 1198 | "es-module-lexer": "^0.9.0", 1199 | "eslint-scope": "5.1.1", 1200 | "events": "^3.2.0", 1201 | "glob-to-regexp": "^0.4.1", 1202 | "graceful-fs": "^4.2.9", 1203 | "json-parse-even-better-errors": "^2.3.1", 1204 | "loader-runner": "^4.2.0", 1205 | "mime-types": "^2.1.27", 1206 | "neo-async": "^2.6.2", 1207 | "schema-utils": "^3.1.0", 1208 | "tapable": "^2.1.1", 1209 | "terser-webpack-plugin": "^5.1.3", 1210 | "watchpack": "^2.4.0", 1211 | "webpack-sources": "^3.2.3" 1212 | }, 1213 | "bin": { 1214 | "webpack": "bin/webpack.js" 1215 | }, 1216 | "engines": { 1217 | "node": ">=10.13.0" 1218 | }, 1219 | "funding": { 1220 | "type": "opencollective", 1221 | "url": "https://opencollective.com/webpack" 1222 | }, 1223 | "peerDependenciesMeta": { 1224 | "webpack-cli": { 1225 | "optional": true 1226 | } 1227 | } 1228 | }, 1229 | "node_modules/webpack-cli": { 1230 | "version": "4.10.0", 1231 | "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", 1232 | "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", 1233 | "dev": true, 1234 | "dependencies": { 1235 | "@discoveryjs/json-ext": "^0.5.0", 1236 | "@webpack-cli/configtest": "^1.2.0", 1237 | "@webpack-cli/info": "^1.5.0", 1238 | "@webpack-cli/serve": "^1.7.0", 1239 | "colorette": "^2.0.14", 1240 | "commander": "^7.0.0", 1241 | "cross-spawn": "^7.0.3", 1242 | "fastest-levenshtein": "^1.0.12", 1243 | "import-local": "^3.0.2", 1244 | "interpret": "^2.2.0", 1245 | "rechoir": "^0.7.0", 1246 | "webpack-merge": "^5.7.3" 1247 | }, 1248 | "bin": { 1249 | "webpack-cli": "bin/cli.js" 1250 | }, 1251 | "engines": { 1252 | "node": ">=10.13.0" 1253 | }, 1254 | "funding": { 1255 | "type": "opencollective", 1256 | "url": "https://opencollective.com/webpack" 1257 | }, 1258 | "peerDependencies": { 1259 | "webpack": "4.x.x || 5.x.x" 1260 | }, 1261 | "peerDependenciesMeta": { 1262 | "@webpack-cli/generators": { 1263 | "optional": true 1264 | }, 1265 | "@webpack-cli/migrate": { 1266 | "optional": true 1267 | }, 1268 | "webpack-bundle-analyzer": { 1269 | "optional": true 1270 | }, 1271 | "webpack-dev-server": { 1272 | "optional": true 1273 | } 1274 | } 1275 | }, 1276 | "node_modules/webpack-cli/node_modules/commander": { 1277 | "version": "7.2.0", 1278 | "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", 1279 | "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", 1280 | "dev": true, 1281 | "engines": { 1282 | "node": ">= 10" 1283 | } 1284 | }, 1285 | "node_modules/webpack-merge": { 1286 | "version": "5.8.0", 1287 | "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", 1288 | "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", 1289 | "dev": true, 1290 | "dependencies": { 1291 | "clone-deep": "^4.0.1", 1292 | "wildcard": "^2.0.0" 1293 | }, 1294 | "engines": { 1295 | "node": ">=10.0.0" 1296 | } 1297 | }, 1298 | "node_modules/webpack-sources": { 1299 | "version": "3.2.3", 1300 | "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", 1301 | "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", 1302 | "dev": true, 1303 | "engines": { 1304 | "node": ">=10.13.0" 1305 | } 1306 | }, 1307 | "node_modules/which": { 1308 | "version": "2.0.2", 1309 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1310 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1311 | "dev": true, 1312 | "dependencies": { 1313 | "isexe": "^2.0.0" 1314 | }, 1315 | "bin": { 1316 | "node-which": "bin/node-which" 1317 | }, 1318 | "engines": { 1319 | "node": ">= 8" 1320 | } 1321 | }, 1322 | "node_modules/wildcard": { 1323 | "version": "2.0.0", 1324 | "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", 1325 | "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", 1326 | "dev": true 1327 | } 1328 | }, 1329 | "dependencies": { 1330 | "@discoveryjs/json-ext": { 1331 | "version": "0.5.7", 1332 | "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", 1333 | "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", 1334 | "dev": true 1335 | }, 1336 | "@jridgewell/gen-mapping": { 1337 | "version": "0.3.2", 1338 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 1339 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 1340 | "dev": true, 1341 | "requires": { 1342 | "@jridgewell/set-array": "^1.0.1", 1343 | "@jridgewell/sourcemap-codec": "^1.4.10", 1344 | "@jridgewell/trace-mapping": "^0.3.9" 1345 | } 1346 | }, 1347 | "@jridgewell/resolve-uri": { 1348 | "version": "3.1.0", 1349 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 1350 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 1351 | "dev": true 1352 | }, 1353 | "@jridgewell/set-array": { 1354 | "version": "1.1.2", 1355 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 1356 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 1357 | "dev": true 1358 | }, 1359 | "@jridgewell/source-map": { 1360 | "version": "0.3.2", 1361 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", 1362 | "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", 1363 | "dev": true, 1364 | "requires": { 1365 | "@jridgewell/gen-mapping": "^0.3.0", 1366 | "@jridgewell/trace-mapping": "^0.3.9" 1367 | } 1368 | }, 1369 | "@jridgewell/sourcemap-codec": { 1370 | "version": "1.4.14", 1371 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 1372 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 1373 | "dev": true 1374 | }, 1375 | "@jridgewell/trace-mapping": { 1376 | "version": "0.3.15", 1377 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", 1378 | "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", 1379 | "dev": true, 1380 | "requires": { 1381 | "@jridgewell/resolve-uri": "^3.0.3", 1382 | "@jridgewell/sourcemap-codec": "^1.4.10" 1383 | } 1384 | }, 1385 | "@types/eslint": { 1386 | "version": "8.4.6", 1387 | "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", 1388 | "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", 1389 | "dev": true, 1390 | "requires": { 1391 | "@types/estree": "*", 1392 | "@types/json-schema": "*" 1393 | } 1394 | }, 1395 | "@types/eslint-scope": { 1396 | "version": "3.7.4", 1397 | "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", 1398 | "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", 1399 | "dev": true, 1400 | "requires": { 1401 | "@types/eslint": "*", 1402 | "@types/estree": "*" 1403 | } 1404 | }, 1405 | "@types/estree": { 1406 | "version": "0.0.51", 1407 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", 1408 | "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", 1409 | "dev": true 1410 | }, 1411 | "@types/json-schema": { 1412 | "version": "7.0.11", 1413 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", 1414 | "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", 1415 | "dev": true 1416 | }, 1417 | "@types/node": { 1418 | "version": "18.7.18", 1419 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", 1420 | "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", 1421 | "dev": true 1422 | }, 1423 | "@webassemblyjs/ast": { 1424 | "version": "1.11.1", 1425 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", 1426 | "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", 1427 | "dev": true, 1428 | "requires": { 1429 | "@webassemblyjs/helper-numbers": "1.11.1", 1430 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1" 1431 | } 1432 | }, 1433 | "@webassemblyjs/floating-point-hex-parser": { 1434 | "version": "1.11.1", 1435 | "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", 1436 | "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", 1437 | "dev": true 1438 | }, 1439 | "@webassemblyjs/helper-api-error": { 1440 | "version": "1.11.1", 1441 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", 1442 | "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", 1443 | "dev": true 1444 | }, 1445 | "@webassemblyjs/helper-buffer": { 1446 | "version": "1.11.1", 1447 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", 1448 | "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", 1449 | "dev": true 1450 | }, 1451 | "@webassemblyjs/helper-numbers": { 1452 | "version": "1.11.1", 1453 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", 1454 | "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", 1455 | "dev": true, 1456 | "requires": { 1457 | "@webassemblyjs/floating-point-hex-parser": "1.11.1", 1458 | "@webassemblyjs/helper-api-error": "1.11.1", 1459 | "@xtuc/long": "4.2.2" 1460 | } 1461 | }, 1462 | "@webassemblyjs/helper-wasm-bytecode": { 1463 | "version": "1.11.1", 1464 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", 1465 | "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", 1466 | "dev": true 1467 | }, 1468 | "@webassemblyjs/helper-wasm-section": { 1469 | "version": "1.11.1", 1470 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", 1471 | "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", 1472 | "dev": true, 1473 | "requires": { 1474 | "@webassemblyjs/ast": "1.11.1", 1475 | "@webassemblyjs/helper-buffer": "1.11.1", 1476 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 1477 | "@webassemblyjs/wasm-gen": "1.11.1" 1478 | } 1479 | }, 1480 | "@webassemblyjs/ieee754": { 1481 | "version": "1.11.1", 1482 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", 1483 | "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", 1484 | "dev": true, 1485 | "requires": { 1486 | "@xtuc/ieee754": "^1.2.0" 1487 | } 1488 | }, 1489 | "@webassemblyjs/leb128": { 1490 | "version": "1.11.1", 1491 | "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", 1492 | "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", 1493 | "dev": true, 1494 | "requires": { 1495 | "@xtuc/long": "4.2.2" 1496 | } 1497 | }, 1498 | "@webassemblyjs/utf8": { 1499 | "version": "1.11.1", 1500 | "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", 1501 | "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", 1502 | "dev": true 1503 | }, 1504 | "@webassemblyjs/wasm-edit": { 1505 | "version": "1.11.1", 1506 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", 1507 | "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", 1508 | "dev": true, 1509 | "requires": { 1510 | "@webassemblyjs/ast": "1.11.1", 1511 | "@webassemblyjs/helper-buffer": "1.11.1", 1512 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 1513 | "@webassemblyjs/helper-wasm-section": "1.11.1", 1514 | "@webassemblyjs/wasm-gen": "1.11.1", 1515 | "@webassemblyjs/wasm-opt": "1.11.1", 1516 | "@webassemblyjs/wasm-parser": "1.11.1", 1517 | "@webassemblyjs/wast-printer": "1.11.1" 1518 | } 1519 | }, 1520 | "@webassemblyjs/wasm-gen": { 1521 | "version": "1.11.1", 1522 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", 1523 | "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", 1524 | "dev": true, 1525 | "requires": { 1526 | "@webassemblyjs/ast": "1.11.1", 1527 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 1528 | "@webassemblyjs/ieee754": "1.11.1", 1529 | "@webassemblyjs/leb128": "1.11.1", 1530 | "@webassemblyjs/utf8": "1.11.1" 1531 | } 1532 | }, 1533 | "@webassemblyjs/wasm-opt": { 1534 | "version": "1.11.1", 1535 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", 1536 | "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", 1537 | "dev": true, 1538 | "requires": { 1539 | "@webassemblyjs/ast": "1.11.1", 1540 | "@webassemblyjs/helper-buffer": "1.11.1", 1541 | "@webassemblyjs/wasm-gen": "1.11.1", 1542 | "@webassemblyjs/wasm-parser": "1.11.1" 1543 | } 1544 | }, 1545 | "@webassemblyjs/wasm-parser": { 1546 | "version": "1.11.1", 1547 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", 1548 | "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", 1549 | "dev": true, 1550 | "requires": { 1551 | "@webassemblyjs/ast": "1.11.1", 1552 | "@webassemblyjs/helper-api-error": "1.11.1", 1553 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1", 1554 | "@webassemblyjs/ieee754": "1.11.1", 1555 | "@webassemblyjs/leb128": "1.11.1", 1556 | "@webassemblyjs/utf8": "1.11.1" 1557 | } 1558 | }, 1559 | "@webassemblyjs/wast-printer": { 1560 | "version": "1.11.1", 1561 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", 1562 | "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", 1563 | "dev": true, 1564 | "requires": { 1565 | "@webassemblyjs/ast": "1.11.1", 1566 | "@xtuc/long": "4.2.2" 1567 | } 1568 | }, 1569 | "@webpack-cli/configtest": { 1570 | "version": "1.2.0", 1571 | "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", 1572 | "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", 1573 | "dev": true, 1574 | "requires": {} 1575 | }, 1576 | "@webpack-cli/info": { 1577 | "version": "1.5.0", 1578 | "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", 1579 | "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", 1580 | "dev": true, 1581 | "requires": { 1582 | "envinfo": "^7.7.3" 1583 | } 1584 | }, 1585 | "@webpack-cli/serve": { 1586 | "version": "1.7.0", 1587 | "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", 1588 | "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", 1589 | "dev": true, 1590 | "requires": {} 1591 | }, 1592 | "@xtuc/ieee754": { 1593 | "version": "1.2.0", 1594 | "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", 1595 | "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", 1596 | "dev": true 1597 | }, 1598 | "@xtuc/long": { 1599 | "version": "4.2.2", 1600 | "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", 1601 | "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", 1602 | "dev": true 1603 | }, 1604 | "acorn": { 1605 | "version": "8.8.0", 1606 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", 1607 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", 1608 | "dev": true 1609 | }, 1610 | "acorn-import-assertions": { 1611 | "version": "1.8.0", 1612 | "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", 1613 | "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", 1614 | "dev": true, 1615 | "requires": {} 1616 | }, 1617 | "ajv": { 1618 | "version": "6.12.6", 1619 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 1620 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 1621 | "dev": true, 1622 | "requires": { 1623 | "fast-deep-equal": "^3.1.1", 1624 | "fast-json-stable-stringify": "^2.0.0", 1625 | "json-schema-traverse": "^0.4.1", 1626 | "uri-js": "^4.2.2" 1627 | } 1628 | }, 1629 | "ajv-keywords": { 1630 | "version": "3.5.2", 1631 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", 1632 | "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", 1633 | "dev": true, 1634 | "requires": {} 1635 | }, 1636 | "browserslist": { 1637 | "version": "4.21.4", 1638 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", 1639 | "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", 1640 | "dev": true, 1641 | "requires": { 1642 | "caniuse-lite": "^1.0.30001400", 1643 | "electron-to-chromium": "^1.4.251", 1644 | "node-releases": "^2.0.6", 1645 | "update-browserslist-db": "^1.0.9" 1646 | } 1647 | }, 1648 | "buffer-from": { 1649 | "version": "1.1.2", 1650 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 1651 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 1652 | "dev": true 1653 | }, 1654 | "caniuse-lite": { 1655 | "version": "1.0.30001409", 1656 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", 1657 | "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", 1658 | "dev": true 1659 | }, 1660 | "chrome-trace-event": { 1661 | "version": "1.0.3", 1662 | "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", 1663 | "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", 1664 | "dev": true 1665 | }, 1666 | "clone-deep": { 1667 | "version": "4.0.1", 1668 | "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", 1669 | "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", 1670 | "dev": true, 1671 | "requires": { 1672 | "is-plain-object": "^2.0.4", 1673 | "kind-of": "^6.0.2", 1674 | "shallow-clone": "^3.0.0" 1675 | } 1676 | }, 1677 | "colorette": { 1678 | "version": "2.0.19", 1679 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", 1680 | "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", 1681 | "dev": true 1682 | }, 1683 | "commander": { 1684 | "version": "2.20.3", 1685 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 1686 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 1687 | "dev": true 1688 | }, 1689 | "cross-spawn": { 1690 | "version": "7.0.3", 1691 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 1692 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 1693 | "dev": true, 1694 | "requires": { 1695 | "path-key": "^3.1.0", 1696 | "shebang-command": "^2.0.0", 1697 | "which": "^2.0.1" 1698 | } 1699 | }, 1700 | "electron-to-chromium": { 1701 | "version": "1.4.256", 1702 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", 1703 | "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", 1704 | "dev": true 1705 | }, 1706 | "enhanced-resolve": { 1707 | "version": "5.10.0", 1708 | "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", 1709 | "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", 1710 | "dev": true, 1711 | "requires": { 1712 | "graceful-fs": "^4.2.4", 1713 | "tapable": "^2.2.0" 1714 | } 1715 | }, 1716 | "envinfo": { 1717 | "version": "7.8.1", 1718 | "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", 1719 | "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", 1720 | "dev": true 1721 | }, 1722 | "es-module-lexer": { 1723 | "version": "0.9.3", 1724 | "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", 1725 | "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", 1726 | "dev": true 1727 | }, 1728 | "escalade": { 1729 | "version": "3.1.1", 1730 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1731 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1732 | "dev": true 1733 | }, 1734 | "eslint-scope": { 1735 | "version": "5.1.1", 1736 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 1737 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 1738 | "dev": true, 1739 | "requires": { 1740 | "esrecurse": "^4.3.0", 1741 | "estraverse": "^4.1.1" 1742 | } 1743 | }, 1744 | "esrecurse": { 1745 | "version": "4.3.0", 1746 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 1747 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1748 | "dev": true, 1749 | "requires": { 1750 | "estraverse": "^5.2.0" 1751 | }, 1752 | "dependencies": { 1753 | "estraverse": { 1754 | "version": "5.3.0", 1755 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1756 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1757 | "dev": true 1758 | } 1759 | } 1760 | }, 1761 | "estraverse": { 1762 | "version": "4.3.0", 1763 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 1764 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 1765 | "dev": true 1766 | }, 1767 | "events": { 1768 | "version": "3.3.0", 1769 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 1770 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 1771 | "dev": true 1772 | }, 1773 | "fast-deep-equal": { 1774 | "version": "3.1.3", 1775 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1776 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 1777 | "dev": true 1778 | }, 1779 | "fast-json-stable-stringify": { 1780 | "version": "2.1.0", 1781 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1782 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1783 | "dev": true 1784 | }, 1785 | "fastest-levenshtein": { 1786 | "version": "1.0.16", 1787 | "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", 1788 | "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", 1789 | "dev": true 1790 | }, 1791 | "find-up": { 1792 | "version": "4.1.0", 1793 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 1794 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 1795 | "dev": true, 1796 | "requires": { 1797 | "locate-path": "^5.0.0", 1798 | "path-exists": "^4.0.0" 1799 | } 1800 | }, 1801 | "function-bind": { 1802 | "version": "1.1.1", 1803 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1804 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1805 | "dev": true 1806 | }, 1807 | "glob-to-regexp": { 1808 | "version": "0.4.1", 1809 | "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 1810 | "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 1811 | "dev": true 1812 | }, 1813 | "graceful-fs": { 1814 | "version": "4.2.10", 1815 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 1816 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", 1817 | "dev": true 1818 | }, 1819 | "has": { 1820 | "version": "1.0.3", 1821 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1822 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1823 | "dev": true, 1824 | "requires": { 1825 | "function-bind": "^1.1.1" 1826 | } 1827 | }, 1828 | "has-flag": { 1829 | "version": "4.0.0", 1830 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1831 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1832 | "dev": true 1833 | }, 1834 | "import-local": { 1835 | "version": "3.1.0", 1836 | "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", 1837 | "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", 1838 | "dev": true, 1839 | "requires": { 1840 | "pkg-dir": "^4.2.0", 1841 | "resolve-cwd": "^3.0.0" 1842 | } 1843 | }, 1844 | "interpret": { 1845 | "version": "2.2.0", 1846 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", 1847 | "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", 1848 | "dev": true 1849 | }, 1850 | "is-core-module": { 1851 | "version": "2.10.0", 1852 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", 1853 | "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", 1854 | "dev": true, 1855 | "requires": { 1856 | "has": "^1.0.3" 1857 | } 1858 | }, 1859 | "is-plain-object": { 1860 | "version": "2.0.4", 1861 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 1862 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", 1863 | "dev": true, 1864 | "requires": { 1865 | "isobject": "^3.0.1" 1866 | } 1867 | }, 1868 | "isexe": { 1869 | "version": "2.0.0", 1870 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1871 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1872 | "dev": true 1873 | }, 1874 | "isobject": { 1875 | "version": "3.0.1", 1876 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 1877 | "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", 1878 | "dev": true 1879 | }, 1880 | "jest-worker": { 1881 | "version": "27.5.1", 1882 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", 1883 | "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", 1884 | "dev": true, 1885 | "requires": { 1886 | "@types/node": "*", 1887 | "merge-stream": "^2.0.0", 1888 | "supports-color": "^8.0.0" 1889 | } 1890 | }, 1891 | "json-parse-even-better-errors": { 1892 | "version": "2.3.1", 1893 | "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", 1894 | "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", 1895 | "dev": true 1896 | }, 1897 | "json-schema-traverse": { 1898 | "version": "0.4.1", 1899 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1900 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1901 | "dev": true 1902 | }, 1903 | "kind-of": { 1904 | "version": "6.0.3", 1905 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 1906 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", 1907 | "dev": true 1908 | }, 1909 | "loader-runner": { 1910 | "version": "4.3.0", 1911 | "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", 1912 | "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", 1913 | "dev": true 1914 | }, 1915 | "locate-path": { 1916 | "version": "5.0.0", 1917 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 1918 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 1919 | "dev": true, 1920 | "requires": { 1921 | "p-locate": "^4.1.0" 1922 | } 1923 | }, 1924 | "merge-stream": { 1925 | "version": "2.0.0", 1926 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 1927 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 1928 | "dev": true 1929 | }, 1930 | "mime-db": { 1931 | "version": "1.52.0", 1932 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1933 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1934 | "dev": true 1935 | }, 1936 | "mime-types": { 1937 | "version": "2.1.35", 1938 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1939 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1940 | "dev": true, 1941 | "requires": { 1942 | "mime-db": "1.52.0" 1943 | } 1944 | }, 1945 | "neo-async": { 1946 | "version": "2.6.2", 1947 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", 1948 | "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", 1949 | "dev": true 1950 | }, 1951 | "node-releases": { 1952 | "version": "2.0.6", 1953 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", 1954 | "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", 1955 | "dev": true 1956 | }, 1957 | "p-limit": { 1958 | "version": "2.3.0", 1959 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 1960 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 1961 | "dev": true, 1962 | "requires": { 1963 | "p-try": "^2.0.0" 1964 | } 1965 | }, 1966 | "p-locate": { 1967 | "version": "4.1.0", 1968 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1969 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1970 | "dev": true, 1971 | "requires": { 1972 | "p-limit": "^2.2.0" 1973 | } 1974 | }, 1975 | "p-try": { 1976 | "version": "2.2.0", 1977 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1978 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 1979 | "dev": true 1980 | }, 1981 | "path-exists": { 1982 | "version": "4.0.0", 1983 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1984 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1985 | "dev": true 1986 | }, 1987 | "path-key": { 1988 | "version": "3.1.1", 1989 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1990 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1991 | "dev": true 1992 | }, 1993 | "path-parse": { 1994 | "version": "1.0.7", 1995 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1996 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1997 | "dev": true 1998 | }, 1999 | "picocolors": { 2000 | "version": "1.0.0", 2001 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 2002 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 2003 | "dev": true 2004 | }, 2005 | "pkg-dir": { 2006 | "version": "4.2.0", 2007 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 2008 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 2009 | "dev": true, 2010 | "requires": { 2011 | "find-up": "^4.0.0" 2012 | } 2013 | }, 2014 | "punycode": { 2015 | "version": "2.1.1", 2016 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 2017 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 2018 | "dev": true 2019 | }, 2020 | "randombytes": { 2021 | "version": "2.1.0", 2022 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 2023 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 2024 | "dev": true, 2025 | "requires": { 2026 | "safe-buffer": "^5.1.0" 2027 | } 2028 | }, 2029 | "rechoir": { 2030 | "version": "0.7.1", 2031 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", 2032 | "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", 2033 | "dev": true, 2034 | "requires": { 2035 | "resolve": "^1.9.0" 2036 | } 2037 | }, 2038 | "resolve": { 2039 | "version": "1.22.1", 2040 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 2041 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 2042 | "dev": true, 2043 | "requires": { 2044 | "is-core-module": "^2.9.0", 2045 | "path-parse": "^1.0.7", 2046 | "supports-preserve-symlinks-flag": "^1.0.0" 2047 | } 2048 | }, 2049 | "resolve-cwd": { 2050 | "version": "3.0.0", 2051 | "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", 2052 | "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", 2053 | "dev": true, 2054 | "requires": { 2055 | "resolve-from": "^5.0.0" 2056 | } 2057 | }, 2058 | "resolve-from": { 2059 | "version": "5.0.0", 2060 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", 2061 | "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", 2062 | "dev": true 2063 | }, 2064 | "safe-buffer": { 2065 | "version": "5.2.1", 2066 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 2067 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 2068 | "dev": true 2069 | }, 2070 | "schema-utils": { 2071 | "version": "3.1.1", 2072 | "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", 2073 | "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", 2074 | "dev": true, 2075 | "requires": { 2076 | "@types/json-schema": "^7.0.8", 2077 | "ajv": "^6.12.5", 2078 | "ajv-keywords": "^3.5.2" 2079 | } 2080 | }, 2081 | "serialize-javascript": { 2082 | "version": "6.0.0", 2083 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 2084 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 2085 | "dev": true, 2086 | "requires": { 2087 | "randombytes": "^2.1.0" 2088 | } 2089 | }, 2090 | "shallow-clone": { 2091 | "version": "3.0.1", 2092 | "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", 2093 | "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", 2094 | "dev": true, 2095 | "requires": { 2096 | "kind-of": "^6.0.2" 2097 | } 2098 | }, 2099 | "shebang-command": { 2100 | "version": "2.0.0", 2101 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2102 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2103 | "dev": true, 2104 | "requires": { 2105 | "shebang-regex": "^3.0.0" 2106 | } 2107 | }, 2108 | "shebang-regex": { 2109 | "version": "3.0.0", 2110 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2111 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2112 | "dev": true 2113 | }, 2114 | "signature_pad": { 2115 | "version": "4.0.0", 2116 | "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-4.0.0.tgz", 2117 | "integrity": "sha512-47I2MULJIHaUAFuVbqMVWwZR+GEl2wH4QKg16OwpQg82GzVUL3Z405Um6sLgVB0xbP5rwqiNzfLTbJwipXvaWA==" 2118 | }, 2119 | "source-map": { 2120 | "version": "0.6.1", 2121 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2122 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2123 | "dev": true 2124 | }, 2125 | "source-map-support": { 2126 | "version": "0.5.21", 2127 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 2128 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 2129 | "dev": true, 2130 | "requires": { 2131 | "buffer-from": "^1.0.0", 2132 | "source-map": "^0.6.0" 2133 | } 2134 | }, 2135 | "supports-color": { 2136 | "version": "8.1.1", 2137 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 2138 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 2139 | "dev": true, 2140 | "requires": { 2141 | "has-flag": "^4.0.0" 2142 | } 2143 | }, 2144 | "supports-preserve-symlinks-flag": { 2145 | "version": "1.0.0", 2146 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 2147 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 2148 | "dev": true 2149 | }, 2150 | "tapable": { 2151 | "version": "2.2.1", 2152 | "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", 2153 | "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", 2154 | "dev": true 2155 | }, 2156 | "terser": { 2157 | "version": "5.15.0", 2158 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", 2159 | "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", 2160 | "dev": true, 2161 | "requires": { 2162 | "@jridgewell/source-map": "^0.3.2", 2163 | "acorn": "^8.5.0", 2164 | "commander": "^2.20.0", 2165 | "source-map-support": "~0.5.20" 2166 | } 2167 | }, 2168 | "terser-webpack-plugin": { 2169 | "version": "5.3.6", 2170 | "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", 2171 | "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", 2172 | "dev": true, 2173 | "requires": { 2174 | "@jridgewell/trace-mapping": "^0.3.14", 2175 | "jest-worker": "^27.4.5", 2176 | "schema-utils": "^3.1.1", 2177 | "serialize-javascript": "^6.0.0", 2178 | "terser": "^5.14.1" 2179 | } 2180 | }, 2181 | "update-browserslist-db": { 2182 | "version": "1.0.9", 2183 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", 2184 | "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", 2185 | "dev": true, 2186 | "requires": { 2187 | "escalade": "^3.1.1", 2188 | "picocolors": "^1.0.0" 2189 | } 2190 | }, 2191 | "uri-js": { 2192 | "version": "4.4.1", 2193 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2194 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2195 | "dev": true, 2196 | "requires": { 2197 | "punycode": "^2.1.0" 2198 | } 2199 | }, 2200 | "watchpack": { 2201 | "version": "2.4.0", 2202 | "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", 2203 | "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", 2204 | "dev": true, 2205 | "requires": { 2206 | "glob-to-regexp": "^0.4.1", 2207 | "graceful-fs": "^4.1.2" 2208 | } 2209 | }, 2210 | "webpack": { 2211 | "version": "5.74.0", 2212 | "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", 2213 | "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", 2214 | "dev": true, 2215 | "requires": { 2216 | "@types/eslint-scope": "^3.7.3", 2217 | "@types/estree": "^0.0.51", 2218 | "@webassemblyjs/ast": "1.11.1", 2219 | "@webassemblyjs/wasm-edit": "1.11.1", 2220 | "@webassemblyjs/wasm-parser": "1.11.1", 2221 | "acorn": "^8.7.1", 2222 | "acorn-import-assertions": "^1.7.6", 2223 | "browserslist": "^4.14.5", 2224 | "chrome-trace-event": "^1.0.2", 2225 | "enhanced-resolve": "^5.10.0", 2226 | "es-module-lexer": "^0.9.0", 2227 | "eslint-scope": "5.1.1", 2228 | "events": "^3.2.0", 2229 | "glob-to-regexp": "^0.4.1", 2230 | "graceful-fs": "^4.2.9", 2231 | "json-parse-even-better-errors": "^2.3.1", 2232 | "loader-runner": "^4.2.0", 2233 | "mime-types": "^2.1.27", 2234 | "neo-async": "^2.6.2", 2235 | "schema-utils": "^3.1.0", 2236 | "tapable": "^2.1.1", 2237 | "terser-webpack-plugin": "^5.1.3", 2238 | "watchpack": "^2.4.0", 2239 | "webpack-sources": "^3.2.3" 2240 | } 2241 | }, 2242 | "webpack-cli": { 2243 | "version": "4.10.0", 2244 | "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", 2245 | "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", 2246 | "dev": true, 2247 | "requires": { 2248 | "@discoveryjs/json-ext": "^0.5.0", 2249 | "@webpack-cli/configtest": "^1.2.0", 2250 | "@webpack-cli/info": "^1.5.0", 2251 | "@webpack-cli/serve": "^1.7.0", 2252 | "colorette": "^2.0.14", 2253 | "commander": "^7.0.0", 2254 | "cross-spawn": "^7.0.3", 2255 | "fastest-levenshtein": "^1.0.12", 2256 | "import-local": "^3.0.2", 2257 | "interpret": "^2.2.0", 2258 | "rechoir": "^0.7.0", 2259 | "webpack-merge": "^5.7.3" 2260 | }, 2261 | "dependencies": { 2262 | "commander": { 2263 | "version": "7.2.0", 2264 | "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", 2265 | "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", 2266 | "dev": true 2267 | } 2268 | } 2269 | }, 2270 | "webpack-merge": { 2271 | "version": "5.8.0", 2272 | "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", 2273 | "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", 2274 | "dev": true, 2275 | "requires": { 2276 | "clone-deep": "^4.0.1", 2277 | "wildcard": "^2.0.0" 2278 | } 2279 | }, 2280 | "webpack-sources": { 2281 | "version": "3.2.3", 2282 | "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", 2283 | "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", 2284 | "dev": true 2285 | }, 2286 | "which": { 2287 | "version": "2.0.2", 2288 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2289 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2290 | "dev": true, 2291 | "requires": { 2292 | "isexe": "^2.0.0" 2293 | } 2294 | }, 2295 | "wildcard": { 2296 | "version": "2.0.0", 2297 | "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", 2298 | "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", 2299 | "dev": true 2300 | } 2301 | } 2302 | } 2303 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-sign-pad", 3 | "version": "1.0.0", 4 | "description": "Laravel package for of E-Signature with Signature Pad and Digital Certified Sign with TCPDF", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack --config webpack.config.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/creagia/laravel-sign-pad.git" 13 | }, 14 | "keywords": [ 15 | "laravel", 16 | "sign", 17 | "pad" 18 | ], 19 | "author": "Creagia", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/creagia/laravel-sign-pad/issues" 23 | }, 24 | "homepage": "https://github.com/creagia/laravel-sign-pad#readme", 25 | "dependencies": { 26 | "signature_pad": "^4.0.0" 27 | }, 28 | "devDependencies": { 29 | "webpack": "^5.74.0", 30 | "webpack-cli": "^4.10.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 8 3 | paths: 4 | - src 5 | - config 6 | tmpDir: build/phpstan 7 | checkOctaneCompatibility: true 8 | checkModelProperties: true 9 | checkMissingIterableValueType: true -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 23 | tests 24 | 25 | 26 | 27 | 28 | ./src 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /resources/assets/sign-pad.js: -------------------------------------------------------------------------------- 1 | import SignaturePad from 'signature_pad'; 2 | 3 | const eSignpads = document.querySelectorAll('.e-signpad'); 4 | 5 | let submitted = false; 6 | 7 | eSignpads.forEach(function(eSignpad) { 8 | let signaturePad = new SignaturePad(eSignpad.querySelector('canvas')), 9 | submit = eSignpad.querySelector('.sign-pad-button-submit'), 10 | clear = eSignpad.querySelector('.sign-pad-button-clear'), 11 | disabledWithoutSignature = parseInt(eSignpad.getAttribute('data-disabled-without-signature')); 12 | 13 | signaturePad.addEventListener("beginStroke", () => { 14 | if(disabledWithoutSignature) { 15 | submit.removeAttribute('disabled'); 16 | } 17 | }); 18 | 19 | submit.addEventListener('click', (event) => { 20 | if (submitted) { 21 | event.preventDefault(); 22 | } 23 | submitted = true; 24 | eSignpad.querySelector('.sign').value = signaturePad.toDataURL(); 25 | }); 26 | 27 | clear.addEventListener('click', () => { 28 | signaturePad.clear(); 29 | if(disabledWithoutSignature) { 30 | submit.setAttribute('disabled', 'disabled'); 31 | } 32 | }) 33 | 34 | }); 35 | 36 | let resizeCanvas = () => { 37 | document.querySelectorAll('.e-signpad').forEach(function(eSignpad) { 38 | let canvas = eSignpad.querySelector('canvas'), 39 | submit = eSignpad.querySelector('.sign-pad-button-submit'), 40 | disabledWithoutSignature = parseInt(eSignpad.getAttribute('data-disabled-without-signature')); 41 | 42 | if (canvas.width > window.innerWidth) { 43 | let signaturePad = new SignaturePad(eSignpad.querySelector('canvas')); 44 | const ratio = Math.max(window.devicePixelRatio || 1, 1); 45 | 46 | if(disabledWithoutSignature) { 47 | submit.setAttribute('disabled', 'disabled'); 48 | } 49 | 50 | signaturePad.addEventListener("beginStroke", () => { 51 | if(disabledWithoutSignature) { 52 | submit.removeAttribute('disabled'); 53 | } 54 | }); 55 | 56 | canvas.width = canvas.offsetWidth * ratio; 57 | canvas.height = canvas.offsetHeight * ratio; 58 | canvas.getContext("2d").scale(ratio, ratio); 59 | signaturePad.clear(); 60 | } 61 | }); 62 | } 63 | 64 | document.addEventListener('DOMContentLoaded', function (event) { 65 | resizeCanvas() 66 | }); 67 | 68 | window.addEventListener("resize", function () { 69 | resizeCanvas() 70 | }); -------------------------------------------------------------------------------- /resources/dist/sign-pad.min.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see sign-pad.min.js.LICENSE.txt */ 2 | (()=>{"use strict";var __webpack_modules__={"./node_modules/signature_pad/dist/signature_pad.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ SignaturePad)\n/* harmony export */ });\n/*!\n * Signature Pad v4.0.0 | https://github.com/szimek/signature_pad\n * (c) 2021 Szymon Nowak | Released under the MIT license\n */\n\nclass Point {\n constructor(x, y, pressure, time) {\n if (isNaN(x) || isNaN(y)) {\n throw new Error(`Point is invalid: (${x}, ${y})`);\n }\n this.x = +x;\n this.y = +y;\n this.pressure = pressure || 0;\n this.time = time || Date.now();\n }\n distanceTo(start) {\n return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));\n }\n equals(other) {\n return (this.x === other.x &&\n this.y === other.y &&\n this.pressure === other.pressure &&\n this.time === other.time);\n }\n velocityFrom(start) {\n return this.time !== start.time\n ? this.distanceTo(start) / (this.time - start.time)\n : 0;\n }\n}\n\nclass Bezier {\n constructor(startPoint, control2, control1, endPoint, startWidth, endWidth) {\n this.startPoint = startPoint;\n this.control2 = control2;\n this.control1 = control1;\n this.endPoint = endPoint;\n this.startWidth = startWidth;\n this.endWidth = endWidth;\n }\n static fromPoints(points, widths) {\n const c2 = this.calculateControlPoints(points[0], points[1], points[2]).c2;\n const c3 = this.calculateControlPoints(points[1], points[2], points[3]).c1;\n return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);\n }\n static calculateControlPoints(s1, s2, s3) {\n const dx1 = s1.x - s2.x;\n const dy1 = s1.y - s2.y;\n const dx2 = s2.x - s3.x;\n const dy2 = s2.y - s3.y;\n const m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };\n const m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };\n const l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n const l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);\n const dxm = m1.x - m2.x;\n const dym = m1.y - m2.y;\n const k = l2 / (l1 + l2);\n const cm = { x: m2.x + dxm * k, y: m2.y + dym * k };\n const tx = s2.x - cm.x;\n const ty = s2.y - cm.y;\n return {\n c1: new Point(m1.x + tx, m1.y + ty),\n c2: new Point(m2.x + tx, m2.y + ty),\n };\n }\n length() {\n const steps = 10;\n let length = 0;\n let px;\n let py;\n for (let i = 0; i <= steps; i += 1) {\n const t = i / steps;\n const cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);\n const cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);\n if (i > 0) {\n const xdiff = cx - px;\n const ydiff = cy - py;\n length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);\n }\n px = cx;\n py = cy;\n }\n return length;\n }\n point(t, start, c1, c2, end) {\n return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))\n + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)\n + (3.0 * c2 * (1.0 - t) * t * t)\n + (end * t * t * t);\n }\n}\n\nfunction throttle(fn, wait = 250) {\n let previous = 0;\n let timeout = null;\n let result;\n let storedContext;\n let storedArgs;\n const later = () => {\n previous = Date.now();\n timeout = null;\n result = fn.apply(storedContext, storedArgs);\n if (!timeout) {\n storedContext = null;\n storedArgs = [];\n }\n };\n return function wrapper(...args) {\n const now = Date.now();\n const remaining = wait - (now - previous);\n storedContext = this;\n storedArgs = args;\n if (remaining <= 0 || remaining > wait) {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n previous = now;\n result = fn.apply(storedContext, storedArgs);\n if (!timeout) {\n storedContext = null;\n storedArgs = [];\n }\n }\n else if (!timeout) {\n timeout = window.setTimeout(later, remaining);\n }\n return result;\n };\n}\n\nclass SignaturePad extends EventTarget {\n constructor(canvas, options = {}) {\n super();\n this.canvas = canvas;\n this.options = options;\n this._handleMouseDown = (event) => {\n if (event.buttons === 1) {\n this._drawningStroke = true;\n this._strokeBegin(event);\n }\n };\n this._handleMouseMove = (event) => {\n if (this._drawningStroke) {\n this._strokeMoveUpdate(event);\n }\n };\n this._handleMouseUp = (event) => {\n if (event.buttons === 1 && this._drawningStroke) {\n this._drawningStroke = false;\n this._strokeEnd(event);\n }\n };\n this._handleTouchStart = (event) => {\n event.preventDefault();\n if (event.targetTouches.length === 1) {\n const touch = event.changedTouches[0];\n this._strokeBegin(touch);\n }\n };\n this._handleTouchMove = (event) => {\n event.preventDefault();\n const touch = event.targetTouches[0];\n this._strokeMoveUpdate(touch);\n };\n this._handleTouchEnd = (event) => {\n const wasCanvasTouched = event.target === this.canvas;\n if (wasCanvasTouched) {\n event.preventDefault();\n const touch = event.changedTouches[0];\n this._strokeEnd(touch);\n }\n };\n this._handlePointerStart = (event) => {\n this._drawningStroke = true;\n event.preventDefault();\n this._strokeBegin(event);\n };\n this._handlePointerMove = (event) => {\n if (this._drawningStroke) {\n event.preventDefault();\n this._strokeMoveUpdate(event);\n }\n };\n this._handlePointerEnd = (event) => {\n this._drawningStroke = false;\n const wasCanvasTouched = event.target === this.canvas;\n if (wasCanvasTouched) {\n event.preventDefault();\n this._strokeEnd(event);\n }\n };\n this.velocityFilterWeight = options.velocityFilterWeight || 0.7;\n this.minWidth = options.minWidth || 0.5;\n this.maxWidth = options.maxWidth || 2.5;\n this.throttle = ('throttle' in options ? options.throttle : 16);\n this.minDistance = ('minDistance' in options ? options.minDistance : 5);\n this.dotSize = options.dotSize || 0;\n this.penColor = options.penColor || 'black';\n this.backgroundColor = options.backgroundColor || 'rgba(0,0,0,0)';\n this._strokeMoveUpdate = this.throttle\n ? throttle(SignaturePad.prototype._strokeUpdate, this.throttle)\n : SignaturePad.prototype._strokeUpdate;\n this._ctx = canvas.getContext('2d');\n this.clear();\n this.on();\n }\n clear() {\n const { _ctx: ctx, canvas } = this;\n ctx.fillStyle = this.backgroundColor;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n this._data = [];\n this._reset();\n this._isEmpty = true;\n }\n fromDataURL(dataUrl, options = {}) {\n return new Promise((resolve, reject) => {\n const image = new Image();\n const ratio = options.ratio || window.devicePixelRatio || 1;\n const width = options.width || this.canvas.width / ratio;\n const height = options.height || this.canvas.height / ratio;\n const xOffset = options.xOffset || 0;\n const yOffset = options.yOffset || 0;\n this._reset();\n image.onload = () => {\n this._ctx.drawImage(image, xOffset, yOffset, width, height);\n resolve();\n };\n image.onerror = (error) => {\n reject(error);\n };\n image.crossOrigin = 'anonymous';\n image.src = dataUrl;\n this._isEmpty = false;\n });\n }\n toDataURL(type = 'image/png', encoderOptions) {\n switch (type) {\n case 'image/svg+xml':\n return this._toSVG();\n default:\n return this.canvas.toDataURL(type, encoderOptions);\n }\n }\n on() {\n this.canvas.style.touchAction = 'none';\n this.canvas.style.msTouchAction = 'none';\n if (window.PointerEvent) {\n this._handlePointerEvents();\n }\n else {\n this._handleMouseEvents();\n if ('ontouchstart' in window) {\n this._handleTouchEvents();\n }\n }\n }\n off() {\n this.canvas.style.touchAction = 'auto';\n this.canvas.style.msTouchAction = 'auto';\n this.canvas.removeEventListener('pointerdown', this._handlePointerStart);\n this.canvas.removeEventListener('pointermove', this._handlePointerMove);\n document.removeEventListener('pointerup', this._handlePointerEnd);\n this.canvas.removeEventListener('mousedown', this._handleMouseDown);\n this.canvas.removeEventListener('mousemove', this._handleMouseMove);\n document.removeEventListener('mouseup', this._handleMouseUp);\n this.canvas.removeEventListener('touchstart', this._handleTouchStart);\n this.canvas.removeEventListener('touchmove', this._handleTouchMove);\n this.canvas.removeEventListener('touchend', this._handleTouchEnd);\n }\n isEmpty() {\n return this._isEmpty;\n }\n fromData(pointGroups, { clear = true } = {}) {\n if (clear) {\n this.clear();\n }\n this._fromData(pointGroups, this._drawCurve.bind(this), this._drawDot.bind(this));\n this._data = clear ? pointGroups : this._data.concat(pointGroups);\n }\n toData() {\n return this._data;\n }\n _strokeBegin(event) {\n this.dispatchEvent(new CustomEvent('beginStroke', { detail: event }));\n const newPointGroup = {\n dotSize: this.dotSize,\n minWidth: this.minWidth,\n maxWidth: this.maxWidth,\n penColor: this.penColor,\n points: [],\n };\n this._data.push(newPointGroup);\n this._reset();\n this._strokeUpdate(event);\n }\n _strokeUpdate(event) {\n if (this._data.length === 0) {\n this._strokeBegin(event);\n return;\n }\n this.dispatchEvent(new CustomEvent('beforeUpdateStroke', { detail: event }));\n const x = event.clientX;\n const y = event.clientY;\n const pressure = event.pressure !== undefined\n ? event.pressure\n : event.force !== undefined\n ? event.force\n : 0;\n const point = this._createPoint(x, y, pressure);\n const lastPointGroup = this._data[this._data.length - 1];\n const lastPoints = lastPointGroup.points;\n const lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];\n const isLastPointTooClose = lastPoint\n ? point.distanceTo(lastPoint) <= this.minDistance\n : false;\n const { penColor, dotSize, minWidth, maxWidth } = lastPointGroup;\n if (!lastPoint || !(lastPoint && isLastPointTooClose)) {\n const curve = this._addPoint(point);\n if (!lastPoint) {\n this._drawDot(point, {\n penColor,\n dotSize,\n minWidth,\n maxWidth,\n });\n }\n else if (curve) {\n this._drawCurve(curve, {\n penColor,\n dotSize,\n minWidth,\n maxWidth,\n });\n }\n lastPoints.push({\n time: point.time,\n x: point.x,\n y: point.y,\n pressure: point.pressure,\n });\n }\n this.dispatchEvent(new CustomEvent('afterUpdateStroke', { detail: event }));\n }\n _strokeEnd(event) {\n this._strokeUpdate(event);\n this.dispatchEvent(new CustomEvent('endStroke', { detail: event }));\n }\n _handlePointerEvents() {\n this._drawningStroke = false;\n this.canvas.addEventListener('pointerdown', this._handlePointerStart);\n this.canvas.addEventListener('pointermove', this._handlePointerMove);\n document.addEventListener('pointerup', this._handlePointerEnd);\n }\n _handleMouseEvents() {\n this._drawningStroke = false;\n this.canvas.addEventListener('mousedown', this._handleMouseDown);\n this.canvas.addEventListener('mousemove', this._handleMouseMove);\n document.addEventListener('mouseup', this._handleMouseUp);\n }\n _handleTouchEvents() {\n this.canvas.addEventListener('touchstart', this._handleTouchStart);\n this.canvas.addEventListener('touchmove', this._handleTouchMove);\n this.canvas.addEventListener('touchend', this._handleTouchEnd);\n }\n _reset() {\n this._lastPoints = [];\n this._lastVelocity = 0;\n this._lastWidth = (this.minWidth + this.maxWidth) / 2;\n this._ctx.fillStyle = this.penColor;\n }\n _createPoint(x, y, pressure) {\n const rect = this.canvas.getBoundingClientRect();\n return new Point(x - rect.left, y - rect.top, pressure, new Date().getTime());\n }\n _addPoint(point) {\n const { _lastPoints } = this;\n _lastPoints.push(point);\n if (_lastPoints.length > 2) {\n if (_lastPoints.length === 3) {\n _lastPoints.unshift(_lastPoints[0]);\n }\n const widths = this._calculateCurveWidths(_lastPoints[1], _lastPoints[2]);\n const curve = Bezier.fromPoints(_lastPoints, widths);\n _lastPoints.shift();\n return curve;\n }\n return null;\n }\n _calculateCurveWidths(startPoint, endPoint) {\n const velocity = this.velocityFilterWeight * endPoint.velocityFrom(startPoint) +\n (1 - this.velocityFilterWeight) * this._lastVelocity;\n const newWidth = this._strokeWidth(velocity);\n const widths = {\n end: newWidth,\n start: this._lastWidth,\n };\n this._lastVelocity = velocity;\n this._lastWidth = newWidth;\n return widths;\n }\n _strokeWidth(velocity) {\n return Math.max(this.maxWidth / (velocity + 1), this.minWidth);\n }\n _drawCurveSegment(x, y, width) {\n const ctx = this._ctx;\n ctx.moveTo(x, y);\n ctx.arc(x, y, width, 0, 2 * Math.PI, false);\n this._isEmpty = false;\n }\n _drawCurve(curve, options) {\n const ctx = this._ctx;\n const widthDelta = curve.endWidth - curve.startWidth;\n const drawSteps = Math.ceil(curve.length()) * 2;\n ctx.beginPath();\n ctx.fillStyle = options.penColor;\n for (let i = 0; i < drawSteps; i += 1) {\n const t = i / drawSteps;\n const tt = t * t;\n const ttt = tt * t;\n const u = 1 - t;\n const uu = u * u;\n const uuu = uu * u;\n let x = uuu * curve.startPoint.x;\n x += 3 * uu * t * curve.control1.x;\n x += 3 * u * tt * curve.control2.x;\n x += ttt * curve.endPoint.x;\n let y = uuu * curve.startPoint.y;\n y += 3 * uu * t * curve.control1.y;\n y += 3 * u * tt * curve.control2.y;\n y += ttt * curve.endPoint.y;\n const width = Math.min(curve.startWidth + ttt * widthDelta, options.maxWidth);\n this._drawCurveSegment(x, y, width);\n }\n ctx.closePath();\n ctx.fill();\n }\n _drawDot(point, options) {\n const ctx = this._ctx;\n const width = options.dotSize > 0\n ? options.dotSize\n : (options.minWidth + options.maxWidth) / 2;\n ctx.beginPath();\n this._drawCurveSegment(point.x, point.y, width);\n ctx.closePath();\n ctx.fillStyle = options.penColor;\n ctx.fill();\n }\n _fromData(pointGroups, drawCurve, drawDot) {\n for (const group of pointGroups) {\n const { penColor, dotSize, minWidth, maxWidth, points } = group;\n if (points.length > 1) {\n for (let j = 0; j < points.length; j += 1) {\n const basicPoint = points[j];\n const point = new Point(basicPoint.x, basicPoint.y, basicPoint.pressure, basicPoint.time);\n this.penColor = penColor;\n if (j === 0) {\n this._reset();\n }\n const curve = this._addPoint(point);\n if (curve) {\n drawCurve(curve, {\n penColor,\n dotSize,\n minWidth,\n maxWidth,\n });\n }\n }\n }\n else {\n this._reset();\n drawDot(points[0], {\n penColor,\n dotSize,\n minWidth,\n maxWidth,\n });\n }\n }\n }\n _toSVG() {\n const pointGroups = this._data;\n const ratio = Math.max(window.devicePixelRatio || 1, 1);\n const minX = 0;\n const minY = 0;\n const maxX = this.canvas.width / ratio;\n const maxY = this.canvas.height / ratio;\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('width', this.canvas.width.toString());\n svg.setAttribute('height', this.canvas.height.toString());\n this._fromData(pointGroups, (curve, { penColor }) => {\n const path = document.createElement('path');\n if (!isNaN(curve.control1.x) &&\n !isNaN(curve.control1.y) &&\n !isNaN(curve.control2.x) &&\n !isNaN(curve.control2.y)) {\n const attr = `M ${curve.startPoint.x.toFixed(3)},${curve.startPoint.y.toFixed(3)} ` +\n `C ${curve.control1.x.toFixed(3)},${curve.control1.y.toFixed(3)} ` +\n `${curve.control2.x.toFixed(3)},${curve.control2.y.toFixed(3)} ` +\n `${curve.endPoint.x.toFixed(3)},${curve.endPoint.y.toFixed(3)}`;\n path.setAttribute('d', attr);\n path.setAttribute('stroke-width', (curve.endWidth * 2.25).toFixed(3));\n path.setAttribute('stroke', penColor);\n path.setAttribute('fill', 'none');\n path.setAttribute('stroke-linecap', 'round');\n svg.appendChild(path);\n }\n }, (point, { penColor, dotSize, minWidth, maxWidth }) => {\n const circle = document.createElement('circle');\n const size = dotSize > 0 ? dotSize : (minWidth + maxWidth) / 2;\n circle.setAttribute('r', size.toString());\n circle.setAttribute('cx', point.x.toString());\n circle.setAttribute('cy', point.y.toString());\n circle.setAttribute('fill', penColor);\n svg.appendChild(circle);\n });\n const prefix = 'data:image/svg+xml;base64,';\n const header = '';\n let body = svg.innerHTML;\n if (body === undefined) {\n const dummy = document.createElement('dummy');\n const nodes = svg.childNodes;\n dummy.innerHTML = '';\n for (let i = 0; i < nodes.length; i += 1) {\n dummy.appendChild(nodes[i].cloneNode(true));\n }\n body = dummy.innerHTML;\n }\n const footer = '';\n const data = header + body + footer;\n return prefix + btoa(data);\n }\n}\n\n\n//# sourceMappingURL=signature_pad.js.map\n\n\n//# sourceURL=webpack://laravel-sign-pad/./node_modules/signature_pad/dist/signature_pad.js?")},"./resources/assets/sign-pad.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var signature_pad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! signature_pad */ \"./node_modules/signature_pad/dist/signature_pad.js\");\n\n\nconst eSignpads = document.querySelectorAll('.e-signpad');\n\nlet submitted = false;\n\neSignpads.forEach(function(eSignpad) {\n let signaturePad = new signature_pad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](eSignpad.querySelector('canvas')),\n submit = eSignpad.querySelector('.sign-pad-button-submit'),\n clear = eSignpad.querySelector('.sign-pad-button-clear'),\n disabledWithoutSignature = parseInt(eSignpad.getAttribute('data-disabled-without-signature'));\n\n signaturePad.addEventListener(\"beginStroke\", () => {\n if(disabledWithoutSignature) {\n submit.removeAttribute('disabled');\n }\n });\n\n submit.addEventListener('click', (event) => {\n if (submitted) {\n event.preventDefault();\n }\n submitted = true;\n eSignpad.querySelector('.sign').value = signaturePad.toDataURL();\n });\n\n clear.addEventListener('click', () => {\n signaturePad.clear();\n if(disabledWithoutSignature) {\n submit.setAttribute('disabled', 'disabled');\n }\n })\n\n});\n\nlet resizeCanvas = () => {\n document.querySelectorAll('.e-signpad').forEach(function(eSignpad) {\n let canvas = eSignpad.querySelector('canvas'),\n submit = eSignpad.querySelector('.sign-pad-button-submit'),\n disabledWithoutSignature = parseInt(eSignpad.getAttribute('data-disabled-without-signature'));\n\n if (canvas.width > window.innerWidth) {\n let signaturePad = new signature_pad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](eSignpad.querySelector('canvas'));\n const ratio = Math.max(window.devicePixelRatio || 1, 1);\n\n if(disabledWithoutSignature) {\n submit.setAttribute('disabled', 'disabled');\n }\n \n signaturePad.addEventListener(\"beginStroke\", () => {\n if(disabledWithoutSignature) {\n submit.removeAttribute('disabled');\n }\n });\n\n canvas.width = canvas.offsetWidth * ratio;\n canvas.height = canvas.offsetHeight * ratio;\n canvas.getContext(\"2d\").scale(ratio, ratio);\n signaturePad.clear();\n }\n });\n}\n\ndocument.addEventListener('DOMContentLoaded', function (event) {\n resizeCanvas()\n});\n\nwindow.addEventListener(\"resize\", function () {\n resizeCanvas()\n});\n\n//# sourceURL=webpack://laravel-sign-pad/./resources/assets/sign-pad.js?")}},__webpack_module_cache__={};function __webpack_require__(t){var n=__webpack_module_cache__[t];if(void 0!==n)return n.exports;var e=__webpack_module_cache__[t]={exports:{}};return __webpack_modules__[t](e,e.exports,__webpack_require__),e.exports}__webpack_require__.d=(t,n)=>{for(var e in n)__webpack_require__.o(n,e)&&!__webpack_require__.o(t,e)&&Object.defineProperty(t,e,{enumerable:!0,get:n[e]})},__webpack_require__.o=(t,n)=>Object.prototype.hasOwnProperty.call(t,n),__webpack_require__.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var __webpack_exports__=__webpack_require__("./resources/assets/sign-pad.js")})(); -------------------------------------------------------------------------------- /resources/dist/sign-pad.min.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*!**************************************!*\ 2 | !*** ./resources/assets/sign-pad.js ***! 3 | \**************************************/ 4 | 5 | /*!**********************************************************!*\ 6 | !*** ./node_modules/signature_pad/dist/signature_pad.js ***! 7 | \**********************************************************/ 8 | -------------------------------------------------------------------------------- /resources/views/components/signature-pad.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 | -------------------------------------------------------------------------------- /resources/views/pad.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @csrf 3 |
4 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /resources/views/pdf.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Laravel E-Sign 7 | 8 | 9 |

10 | This is a digitally signed document using the default (example) tcpdf.crt certificate. 11 |

12 | 13 | 14 | -------------------------------------------------------------------------------- /resources/views/template/pdf.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Laravel E-Sign 7 | 8 | 9 |

10 | This is a digitally signed document using the default (example) tcpdf.crt certificate. 11 |

12 | 13 | 14 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | name('sign-pad::signature'); 6 | -------------------------------------------------------------------------------- /src/Actions/AppendSignatureDocumentAction.php: -------------------------------------------------------------------------------- 1 | validateConfig(); 19 | 20 | foreach ($signatureTemplate->signaturePositions as $signaturePosition) { 21 | $pdf->setPage($signaturePosition->signaturePage); 22 | 23 | $pdf->Image( 24 | '@'.$decodedImage, 25 | $signaturePosition->signatureX, 26 | $signaturePosition->signatureY, 27 | config('sign-pad.width') * 0.26458333 / 2, 28 | config('sign-pad.height') * 0.26458333 / 2, 29 | 'PNG' 30 | ); 31 | 32 | if (config('sign-pad.certify_documents')) { 33 | // Define active area for signature 34 | $pdf->setSignatureAppearance( 35 | $signaturePosition->signatureX, 36 | $signaturePosition->signatureY, 37 | config('sign-pad.width') * 0.26458333 / 2, 38 | config('sign-pad.height') * 0.26458333 / 2, 39 | ); 40 | } 41 | } 42 | 43 | return $pdf; 44 | } 45 | 46 | private function validateConfig(): void 47 | { 48 | if (! is_numeric(config('sign-pad.width'))) { 49 | throw new InvalidConfiguration("Invalid sign pad width. Check your config key 'sign-pad.width'"); 50 | } 51 | if (! is_numeric(config('sign-pad.height'))) { 52 | throw new InvalidConfiguration("Invalid sign pad height. Check your config key 'sign-pad.height'"); 53 | } 54 | 55 | if (! is_bool(config('sign-pad.certify_documents'))) { 56 | throw new InvalidConfiguration("Invalid boolean value. Check your config key 'sign-pad.certify_documents'"); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Actions/CertifyDocumentAction.php: -------------------------------------------------------------------------------- 1 | validateConfig(); 19 | 20 | $certificate = 'file://'.config('sign-pad.certificate_file'); 21 | 22 | $pdf->setSignature( 23 | $certificate, 24 | $certificate, 25 | '', 26 | '', 27 | config('sign-pad.cert_type'), 28 | config('sign-pad.certificate_info') 29 | ); 30 | 31 | return $pdf; 32 | } 33 | 34 | private function validateConfig(): void 35 | { 36 | if (! File::exists(config('sign-pad.certificate_file'))) { 37 | throw new InvalidConfiguration("Certificate file doesn't exists. Check your config key 'sign-pad.certificate_file'"); 38 | } 39 | 40 | if (! in_array(config('sign-pad.cert_type'), [1, 2, 3])) { 41 | throw new InvalidConfiguration("Invalid certificate type. Check your config key 'sign-pad.cert_type'"); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Actions/GenerateSignatureDocumentAction.php: -------------------------------------------------------------------------------- 1 | pdf->SetPrintHeader(false); 19 | $this->pdf->SetPrintFooter(false); 20 | $this->pdf->SetMargins(PDF_MARGIN_LEFT - 5, PDF_MARGIN_TOP - 14, PDF_MARGIN_RIGHT - 5); 21 | $this->pdf->SetAutoPageBreak(true, 0); 22 | 23 | if (config('sign-pad.certify_documents')) { 24 | $this->pdf = $signatureDocumentTemplate->template->certify($this->pdf); 25 | } 26 | 27 | $this->pdf = $signatureDocumentTemplate->template->appendPages($this->pdf, $signature); 28 | 29 | $this->pdf = $signatureDocumentTemplate->template->appendSignature($this->pdf, $decodedImage, $signatureDocumentTemplate); 30 | 31 | $destinationFilename = "{$signatureDocumentTemplate->outputPdfPrefix}-{$signature->uuid}.pdf"; 32 | $filePath = config('sign-pad.documents_path').'/'.$destinationFilename; 33 | 34 | Storage::disk(config('sign-pad.disk_name'))->put($filePath, $this->pdf->Output($filePath, 'S')); 35 | 36 | $signature->document_filename = $destinationFilename; 37 | $signature->save(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Commands/InstallCommand.php: -------------------------------------------------------------------------------- 1 | signature = 'sign-pad:install'; 12 | $this->description = 'Install Laravel Sign Pad'; 13 | 14 | parent::__construct(); 15 | } 16 | 17 | public function handle(): void 18 | { 19 | $this->info('Publishing config file...'); 20 | 21 | $this->callSilently('vendor:publish', [ 22 | '--tag' => 'sign-pad-config', 23 | ]); 24 | 25 | $this->info('Publishing migration...'); 26 | 27 | $this->callSilently('vendor:publish', [ 28 | '--tag' => 'sign-pad-migrations', 29 | ]); 30 | 31 | if ($this->confirm('Would you like to run the migrations now?')) { 32 | $this->comment('Running migrations...'); 33 | 34 | $this->call('migrate'); 35 | } 36 | 37 | if ($this->confirm('Would you like to star our repo on GitHub?')) { 38 | $repoUrl = 'https://github.com/creagia/laravel-sign-pad'; 39 | 40 | if (PHP_OS_FAMILY == 'Darwin') { 41 | exec("open {$repoUrl}"); 42 | } 43 | if (PHP_OS_FAMILY == 'Windows') { 44 | exec("start {$repoUrl}"); 45 | } 46 | if (PHP_OS_FAMILY == 'Linux') { 47 | exec("xdg-open {$repoUrl}"); 48 | } 49 | } 50 | 51 | $this->info('sign-pad has been installed!'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Components/SignaturePad.php: -------------------------------------------------------------------------------- 1 | width = $width ?? config('sign-pad.width'); 41 | $this->height = $height ?? config('sign-pad.height'); 42 | 43 | $this->padClasses = $padClasses; 44 | $this->buttonClasses = $buttonClasses; 45 | 46 | $this->borderColor = $borderColor; 47 | 48 | $this->submitName = $submitName; 49 | $this->clearName = $clearName; 50 | 51 | $this->disabledWithoutSignature = $disabledWithoutSignature; 52 | } 53 | 54 | /** 55 | * Get the view / contents that represent the component. 56 | * 57 | * @return \Illuminate\Contracts\View\View|\Closure|string 58 | */ 59 | public function render() 60 | { 61 | return view('laravel-sign-pad::components.signature-pad'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Concerns/RequiresSignature.php: -------------------------------------------------------------------------------- 1 | morphOne(Signature::class, 'model'); 12 | } 13 | 14 | public function getSignatureRoute(): string 15 | { 16 | return route('sign-pad::signature', [ 17 | 'model' => get_class($this), 18 | 'id' => $this->id, 19 | 'token' => md5(config('app.key').get_class($this)), 20 | ]); 21 | } 22 | 23 | public function hasBeenSigned(): bool 24 | { 25 | return ! is_null($this->signature); 26 | } 27 | 28 | public function deleteSignature(): bool 29 | { 30 | return $this->signature?->delete() ?? false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Contracts/CanBeSigned.php: -------------------------------------------------------------------------------- 1 | validate($request, [ 22 | 'model' => ['required'], 23 | 'sign' => ['required'], 24 | 'id' => ['required'], 25 | 'token' => ['required'], 26 | ]); 27 | 28 | $modelClass = $validatedData['model']; 29 | $decodedImage = base64_decode(explode(',', $validatedData['sign'])[1]); 30 | 31 | if (! $decodedImage) { 32 | throw new Exception('Invalid signature'); 33 | } 34 | 35 | $model = app($modelClass)->findOrFail($validatedData['id']); 36 | 37 | $requiredToken = md5(config('app.key').$modelClass); 38 | if ($validatedData['token'] !== $requiredToken) { 39 | abort(403, 'Invalid token'); 40 | } 41 | 42 | if ($model instanceof CanBeSigned && $model->hasBeenSigned()) { 43 | throw new ModelHasAlreadyBeenSigned; 44 | } 45 | 46 | $uuid = Str::uuid()->toString(); 47 | $filename = "{$uuid}.png"; 48 | $signature = $model->signature()->create([ 49 | 'uuid' => $uuid, 50 | 'from_ips' => $request->ips(), 51 | 'filename' => $filename, 52 | 'certified' => config('sign-pad.certify_documents'), 53 | ]); 54 | 55 | Storage::disk(config('sign-pad.disk_name'))->put($signature->getSignatureImagePath(), $decodedImage); 56 | 57 | if ($model instanceof ShouldGenerateSignatureDocument) { 58 | ($generateSignatureDocumentAction)( 59 | $signature, 60 | $model->getSignatureDocumentTemplate(), 61 | $decodedImage 62 | ); 63 | } 64 | 65 | return redirect()->route(config('sign-pad.redirect_route_name'), ['uuid' => $uuid]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidConfiguration.php: -------------------------------------------------------------------------------- 1 | name('laravel-sign-pad') 21 | ->hasConfigFile() 22 | ->hasViews('laravel-sign-pad') 23 | ->hasRoute('web') 24 | ->hasAssets() 25 | ->hasViewComponent('creagia', SignaturePad::class) 26 | ->hasMigration('create_signatures_table') 27 | ->hasCommand(InstallCommand::class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Signature.php: -------------------------------------------------------------------------------- 1 | delete($model->getSignatureImagePath()); 26 | }); 27 | } 28 | 29 | protected $fillable = [ 30 | 'model_type', 31 | 'model_id', 32 | 'uuid', 33 | 'filename', 34 | 'document_filename', 35 | 'from_ips', 36 | 'certified', 37 | ]; 38 | 39 | /** 40 | * @var array 41 | */ 42 | protected $casts = [ 43 | 'from_ips' => 'array', 44 | 'certified' => 'boolean', 45 | ]; 46 | 47 | /** 48 | * @return MorphTo 49 | */ 50 | public function model(): MorphTo 51 | { 52 | return $this->morphTo(); 53 | } 54 | 55 | public function getSignatureImagePath(): string 56 | { 57 | return config('sign-pad.signatures_path').'/'.$this->attributes['filename']; 58 | } 59 | 60 | public function getSignatureImageAbsolutePath(): string 61 | { 62 | return Storage::disk(config('sign-pad.disk_name'))->path(config('sign-pad.signatures_path').'/'.$this->attributes['filename']); 63 | } 64 | 65 | public function getSignedDocumentPath(): ?string 66 | { 67 | return $this->attributes['document_filename'] ? config('sign-pad.documents_path').'/'.$this->attributes['document_filename'] : null; 68 | } 69 | 70 | public function getSignedDocumentAbsolutePath(): ?string 71 | { 72 | return $this->attributes['document_filename'] 73 | ? Storage::disk(config('sign-pad.disk_name'))->path(config('sign-pad.documents_path').'/'.$this->attributes['document_filename']) 74 | : null; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/SignatureDocumentTemplate.php: -------------------------------------------------------------------------------- 1 | $signaturePositions 11 | */ 12 | public function __construct( 13 | public string $outputPdfPrefix, 14 | public DocumentTemplate $template, 15 | public array $signaturePositions = [], 16 | ) {} 17 | } 18 | -------------------------------------------------------------------------------- /src/SignaturePosition.php: -------------------------------------------------------------------------------- 1 | AddPage(); 13 | $html = view($this->path, ['model' => $signature->model]); 14 | $pdf->writeHTML($html, true, false, true, false); 15 | 16 | return $pdf; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Templates/DocumentTemplate.php: -------------------------------------------------------------------------------- 1 | setSourceFile($this->path); 13 | 14 | foreach (range(1, $totalPdfPages) as $pageNumber) { 15 | $pdf->AddPage(); 16 | $tplIdx = $pdf->importPage($pageNumber); 17 | $pdf->useTemplate($tplIdx, ['adjustPageSize' => true]); 18 | } 19 | 20 | return $pdf; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Feature/RequiresSignatureTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(method_exists($testModel, 'signature')); 10 | $this->assertTrue(method_exists($testModel, 'getSignatureRoute')); 11 | $this->assertTrue(method_exists($testModel, 'hasBeenSigned')); 12 | }); 13 | 14 | it('generates a correct URL', function () { 15 | $url = app(TestModel::class)->getSignatureRoute(); 16 | $this->post($url) 17 | ->assertStatus(Response::HTTP_FOUND); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/Feature/SignPadComponentTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($signPadComponent->submitName === 'Submit'); 9 | $this->assertTrue($signPadComponent->clearName === 'Clear'); 10 | $this->assertTrue($signPadComponent->borderColor === '#777777'); 11 | }); 12 | 13 | it('can override default component values', function () { 14 | $signPadComponent = new SignaturePad( 15 | 200, 200, '', '', '#EAEAEA', 'Envia', 'Borra' 16 | ); 17 | 18 | $this->assertTrue($signPadComponent->width === 200); 19 | $this->assertTrue($signPadComponent->height === 200); 20 | $this->assertTrue($signPadComponent->submitName === 'Envia'); 21 | $this->assertTrue($signPadComponent->clearName === 'Borra'); 22 | $this->assertTrue($signPadComponent->borderColor === '#EAEAEA'); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/Feature/SignPadControllerTest.php: -------------------------------------------------------------------------------- 1 | post(route('sign-pad::signature')) 10 | ->assertStatus(Response::HTTP_FOUND); 11 | }); 12 | 13 | it('all parameters are needed', function () { 14 | $this->post(route('sign-pad::signature')) 15 | ->assertSessionHasErrors(); 16 | }); 17 | 18 | it('validates the data', function () { 19 | $this->post( 20 | route('sign-pad::signature'), 21 | [ 22 | 'model' => 'TestModel', 23 | 'sign' => app(TestSignature::class), 24 | 'id' => 1, 25 | 'token' => md5(config('app.key').'TestModel'), 26 | ] 27 | ) 28 | ->assertSessionHasNoErrors() 29 | ->assertStatus(Response::HTTP_INTERNAL_SERVER_ERROR); 30 | }); 31 | 32 | it('stores the signature image and deletes', function () { 33 | Storage::fake(config('sign-pad.disk_name')); 34 | $model1 = TestModel::create(); 35 | $model2 = TestModel::create(); 36 | $sign = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAADICAYAAAA0n5+2AAAAAXNSR0IArs4c6QAAEG1JREFUeF7t3U2odVUZB/C/BObMV6pBkOhAjXJgIYSRYk2CBqERUY3UCo2w1EkfkzSCqElpkGUTjeiDDPKFaJhZGQh92CTKCiIaZikVQR8YT+xLu+M995x733XvXevs34aLvvfss/azfs/i8mfvffY5LzYCBAgQIECAAIGmAuc1Hc1gBAgQIECAAAECEbAsAgIECBAgQIBAYwEBqzGo4QgQIECAAAECApY1QIAAAQIECBBoLCBgNQY1HAECBAgQIEBAwLIGCBAgQIAAAQKNBQSsxqCGI0CAAAECBAgIWNYAAQIECBAgQKCxgIDVGNRwBAgQIECAAAEByxogQIAAAQIECDQWELAagxqOAAECBAgQICBgWQMECBAgQIAAgcYCAlZjUMMRIECAAAECBAQsa4AAAQIECBAg0FhAwGoMajgCBAgQIECAgIBlDRAgQIAAAQIEGgsIWI1BDUeAAAECBAgQELCsAQIECBAgQIBAYwEBqzGo4QgQIECAAAECApY1QIAAAQIECBBoLCBgNQY1HAECBAgQIEBAwLIGCBAgQIAAAQKNBQSsxqCGI0CAAAECBAgIWNYAAQIECBAgQKCxgIDVGNRwBAgQIECAAAEByxogQIAAAQIECDQWELAagxqOAAECBAgQICBgWQMECBAgQIAAgcYCAlZjUMMRIECAAAECBAQsa4AAAQIECBAg0FhAwGoMajgCBAgQIECAgIBlDRAgQIAAAQIEGgsIWI1BDUeAAAECBAgQELCsAQIECBAgQIBAYwEBqzGo4QgQIECAAAECApY1QIAAAQIECBBoLCBgNQY1HAECBAgQIEBAwLIGCBAgQIAAAQKNBQSsxqCGI0CAAAECBAgIWNYAAQIECBAgQKCxgIDVGNRwBAgQIECAAAEByxogQIAAAQIECDQWELAagxqOAAECBAgQICBgWQMECBAgQIAAgcYCAlZjUMMRIECAAAECBAQsa4AAAQIECBAg0FhAwGoMajgCBAgQIECAgIBlDRAgQIAAAQIEGgsIWI1BDUeAAAECBAgQELCsAQIECBAgQIBAYwEBqzGo4QgQIECAAAECApY1QIAAAQIECBBoLCBgNQY1HAECBAgQIEBAwLIGCBAgQIAAAQKNBQSsxqCGI0CAAAECBAgIWNYAAQIECBAgQKCxgIDVGNRwBAgQIECAAAEByxogQIAAAQIECDQWELAagxqOAAECBAgQICBgWQMECBAgQIAAgcYCAlZjUMMRIECAAAECBAQsa4AAAQIECBAg0FhAwGoMajgCBAgQIECAgIBlDRAgQIAAAQIEGgsIWI1BDUeAAAECBAgQELCsAQIECBAgQIBAYwEBqzGo4QgQIECAAAECApY1QIAAAQIECBBoLCBgNQY1HAECBAgQIEBAwLIGCBAgQIAAAQKNBQSsxqCGI0CAAAECBAgIWNYAAQIECBAgQKCxgIDVGNRwBAgQIECAAAEByxogQIAAAQIECDQWELAagxqOAAECBAgQICBgWQMECBAgQIAAgcYCAlZjUMMRWIjAvUneneSCJF9O8q6FzNs0CRAgsJWAgLUVk50IEJgJVLi6Y0Xke0nuT/IwKQIECBBIBCyrgACBwwg8lOSmNW/4RZIrDzOYfQkQILCrAgLWrnbWvAi0F7gnyd0bhv1YktrPRoAAgUULCFiLbr/JE9ha4EySP++z9xNJ/p7k9bPXXp7kqa1HtiMBAgR2UEDA2sGmmhKBYxC4OcmDK+Puna16Q5Lvzl67PcnnjqEGQxIgQGAYAQFrmFYplMCpCjyS5IZZBfNLgS9M8uskF0+v/zTJ1adarYMTIEDglAUErFNugMMTGEBgv8uDFyV5Zlb795NcN/27wtYVA8xLiQQIEDg2AQHr2GgNTGBnBO5M8pnZbM4muXFldp9M8qHZ7/xt2Zn2mwgBAkcR8EfwKGreQ2BZAj9Icu1syp9K8uEVgrrJ/dHZ7+pxDfXYBhsBAgQWKSBgLbLtJk3gUALPzfb+S5JLk/xpQ8Dyt+VQxHYmQGDXBPwR3LWOmg+BtgKrZ6bqie31qcHV7b4kH5h++dskl7Utw2gECBAYS0DAGqtfqiVw0gL1aIZ6RMPeVg8RrU8Qrm7fTPLW6Ze/SXL5SRfqeAQIEOhJQMDqqRtqIdCfQN1XNX+IaJ29qrNYq9utSR6YfvmllVDW36xURIAAgWMWELCOGdjwBAYXWA1Y6/5mzL9GZ91lxMEplE+AAIHtBQSs7a3sSWCJAj9K8tpp4k8nefEahPkZrC8muW2JWOZMgACBPQEBy1ogQOAggfpOwb37qeqTgy9as7MzWNYRAQIEZgICluVAgMBBAj9M8rpph/pi52ucwbJgCBAgsFlAwNpsZA8CSxao+6munwAeW7nhfe5yd5I6i1XbQ0luWTKauRMgQEDAsgYIEDhI4MkkV20RsL6W5B3Tfl9P8k6sBAgQWLKAgLXk7ps7gc0C86e4H3QG67NJ3i9gbQa1BwECyxAQsJbRZ7MkcFSBecA66PlW9ya5YzpIPYh073LhUY/rfQQIEBhaQMAaun2KJ3DsAr9Lcsl0lPo6nDvXHHF+r9ZdSSpw2QgQILBYAQFrsa03cQJbCfw4ydVbBKz5vVrrnva+1QHtRIAAgV0QELB2oYvmQOD4BH6f5OJp+IMe0zC/lPjqJBW4bAQIEFisgIC12NabOIGtBOZnptZdIqzvKqyv1Knt2SRnthrZTgQIENhhAQFrh5tragQaCMwD1rqb1+dPcT+b5MYGxzUEAQIEhhYQsIZun+IJHLvA/NLfW5I8ss8R3eB+7G1wAAIERhMQsEbrmHoJnKzAPGDV09nrKe2rm/uvTrYnjkaAwAACAtYATVIigVMUeCbJhdPx97tEeGuSB6bX/5nk/FOs1aEJECDQjYCA1U0rFEKgS4H55b/9Atb8AaOPJ7m2y1koigABAicsIGCdMLjDERhMYB6w9vsU4fxBpOvu0RpsysolQIDAuQsIWOduaAQCuyww/47BXyZ5xWyy9WnBb03/9niGXV4F5kaAwKEFBKxDk3kDgUUJVIDae+zCH2YPHS2E+dmtg76ncFFgJkuAAIESELCsAwIEDhKY38Q+P4M1P3tV7/f1ONYRAQIEZgICluVAgMBBAm9K8p19/mbM7736eZJXYSRAgACB/wkIWFYDAQIHCcy/BmfvTNXbk7x39iZnr6whAgQIrAgIWJYEAQKbBOYPEq1nXt02e4N7rzbpeZ0AgUUKCFiLbLtJEziUwKeT3LXPO/6V5PIkdbnQRoAAAQIzAQHLciBAYJPAFUl+tc9OH0/y0U1v9joBAgSWKCBgLbHr5kzg8AKPJqn7sfa2p5NclqS+SsdGgAABAisCApYlQYDAJoH6hODPVnb6W5KXCVib6LxOgMBSBQSspXbevAlsJ1Dhqs5endln9yen5185i7Wdpb0IEFiQgIC1oGabKoFDClSoqnB10DOuHkpyyyHHtTsBAgR2XkDA2vkWmyCBIwlUuHpw9jU5e4N8IUk9fPSS2aiPTCHLmawjUXsTAQK7KCBg7WJXzYnAuQvUmambVoZ5bLrRvc5o1fcQXjh7vcJVncmqsGUjQIDA4gUErMUvAQAEnifwiSQfWfltfR1OfYpw7yzVfiGr3vKVJLe7+d2qIkBg6QIC1tJXgPkT+H+Bm6dLg/PfPjuFq7qpfb7VZcQ603XDyu8rhN2T5D64BAgQWKqAgLXUzps3gecL1D1XFbDm21+TXJdkNVzN96n3fD7JBSvvrffUE+DrcqKNAAECixIQsBbVbpMlsK/A25K8b+VBorXjv6f7sOqy36atzmbVWas79tmxglbdm3V2Q1DbdAyvEyBAYBgBAWuYVimUQHOBl0yPYbhyzchHeQRD3ad1b5Kr1oz5jyRfnc5q1U3zvseweVsNSIBADwICVg9dUAOBkxeos1Z1j9RL1xz64ST3n8PlvbpsWD/Xb5haBay6hFhnueqn7vc66HLkyUs5IgECBI4gIGAdAc1bCAwq8OYkVyd5ZZIKWOu2ClcfbHR26dLp0uN7klyT5AVb2tWZrqeS/HH6ROJq6Kob6euSozNgW4LajQCBkxUQsE7W29EInJbAc1sc+CdJvj3dS7XF7kfapR7vcOMUujad3dp0gApZF23ayesECBA4DQEB6zTUHZPAyQq8JskTaw5ZX+J8fpLHk9x2smX992h1z1b9VPCqs131M3+A6aaSKmB5gvwmJa8TIHDiAgLWiZM7IIFTEagbz9+Y5BvT0eu+p54fn1CfSqywVf+tAFZb/f/8exHrJvz6sREgQKA7AQGru5YoiAABAgQIEBhdQMAavYPqJ0CAAAECBLoTELC6a4mCCBAgQIAAgdEFBKzRO6h+AgQIECBAoDsBAau7liiIAAECBAgQGF1AwBq9g+onQIAAAQIEuhMQsLpriYIIECBAgACB0QUErNE7qH4CBAgQIECgOwEBq7uWKIgAAQIECBAYXUDAGr2D6idAgAABAgS6ExCwumuJgggQIECAAIHRBQSs0TuofgIECBAgQKA7AQGru5YoiAABAgQIEBhdQMAavYPqJ0CAAAECBLoTELC6a4mCCBAgQIAAgdEFBKzRO6h+AgQIECBAoDsBAau7liiIAAECBAgQGF1AwBq9g+onQIAAAQIEuhMQsLpriYIIECBAgACB0QUErNE7qH4CBAgQIECgOwEBq7uWKIgAAQIECBAYXUDAGr2D6idAgAABAgS6ExCwumuJgggQIECAAIHRBQSs0TuofgIECBAgQKA7AQGru5YoiAABAgQIEBhdQMAavYPqJ0CAAAECBLoTELC6a4mCCBAgQIAAgdEFBKzRO6h+AgQIECBAoDsBAau7liiIAAECBAgQGF1AwBq9g+onQIAAAQIEuhMQsLpriYIIECBAgACB0QUErNE7qH4CBAgQIECgOwEBq7uWKIgAAQIECBAYXUDAGr2D6idAgAABAgS6ExCwumuJgggQIECAAIHRBQSs0TuofgIECBAgQKA7AQGru5YoiAABAgQIEBhdQMAavYPqJ0CAAAECBLoTELC6a4mCCBAgQIAAgdEFBKzRO6h+AgQIECBAoDsBAau7liiIAAECBAgQGF1AwBq9g+onQIAAAQIEuhMQsLpriYIIECBAgACB0QUErNE7qH4CBAgQIECgOwEBq7uWKIgAAQIECBAYXUDAGr2D6idAgAABAgS6ExCwumuJgggQIECAAIHRBQSs0TuofgIECBAgQKA7AQGru5YoiAABAgQIEBhdQMAavYPqJ0CAAAECBLoTELC6a4mCCBAgQIAAgdEFBKzRO6h+AgQIECBAoDsBAau7liiIAAECBAgQGF1AwBq9g+onQIAAAQIEuhMQsLpriYIIECBAgACB0QUErNE7qH4CBAgQIECgOwEBq7uWKIgAAQIECBAYXUDAGr2D6idAgAABAgS6ExCwumuJgggQIECAAIHRBQSs0TuofgIECBAgQKA7AQGru5YoiAABAgQIEBhdQMAavYPqJ0CAAAECBLoTELC6a4mCCBAgQIAAgdEFBKzRO6h+AgQIECBAoDsBAau7liiIAAECBAgQGF1AwBq9g+onQIAAAQIEuhMQsLpriYIIECBAgACB0QX+A92V6MmOrBouAAAAAElFTkSuQmCC ◀data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAADICAYAAAA0n5+2AAAAAXNSR0IArs4c6QAAEG1JREFUeF7t3U2odVUZB/C/BObMV6pBkOhAjXJgIYSRYk2CBqERUY3UCo2w1EkfkzSCqElpkG'; 37 | 38 | $this->post($model1->getSignatureRoute(), ['sign' => $sign]); 39 | $this->post($model2->getSignatureRoute(), ['sign' => $sign]); 40 | 41 | /** @var \Creagia\LaravelSignPad\Signature $signature1 */ 42 | $signature1 = $model1->signature; 43 | /** @var \Creagia\LaravelSignPad\Signature $signature2 */ 44 | $signature2 = $model2->signature; 45 | 46 | Storage::disk(config('sign-pad.disk_name'))->assertExists($filePath = $signature1->getSignatureImagePath()); 47 | 48 | $content = Storage::disk(config('sign-pad.disk_name'))->get($filePath); 49 | $decodedImage = base64_decode(explode(',', $sign)[1]); 50 | $this->assertSame($decodedImage, $content); 51 | 52 | $signature1->delete(); 53 | $this->assertFileDoesNotExist($filePath); 54 | Storage::disk(config('sign-pad.disk_name'))->assertMissing($filePath); 55 | Storage::disk(config('sign-pad.disk_name'))->assertExists($signature2->getSignatureImagePath()); 56 | }); 57 | -------------------------------------------------------------------------------- /tests/Models/TestModel.php: -------------------------------------------------------------------------------- 1 | in(__DIR__); 6 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 'Creagia\\LaravelSignPad\\Database\\Factories\\'.class_basename($modelName).'Factory' 17 | ); 18 | } 19 | 20 | protected function getPackageProviders($app) 21 | { 22 | return [ 23 | LaravelSignPadServiceProvider::class, 24 | ]; 25 | } 26 | 27 | public function getEnvironmentSetUp($app) 28 | { 29 | config()->set('database.default', 'testing'); 30 | 31 | $migration = include __DIR__.'/migrations/create_test_models_table.php.stub'; 32 | $migration->up(); 33 | 34 | $migration = include __DIR__.'/../database/migrations/create_signatures_table.php.stub'; 35 | $migration->up(); 36 | } 37 | 38 | /** 39 | * Define routes setup. 40 | * 41 | * @param \Illuminate\Routing\Router $router 42 | * @return void 43 | */ 44 | protected function defineRoutes($router) 45 | { 46 | // 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/TestClasses/TestSignature.php: -------------------------------------------------------------------------------- 1 | id(); 13 | $table->timestamps(); 14 | }); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | entry: './resources/assets/sign-pad.js', 6 | output: { 7 | path: path.resolve(__dirname, 'resources/dist'), 8 | filename: 'sign-pad.min.js', 9 | }, 10 | optimization: { 11 | minimize: true, 12 | } 13 | }; --------------------------------------------------------------------------------