diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..5512c35
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,87 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ pull_request:
+
+jobs:
+ compatibility:
+ name: PHP ${{ matrix.php }} / Laravel ${{ matrix.laravel }}
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - php: '8.1'
+ laravel: '10.x'
+ testbench: '^8.37'
+ pest: '^2.36'
+ - php: '8.2'
+ laravel: '11.x'
+ testbench: '^9.17'
+ pest: '^3.8'
+ - php: '8.3'
+ laravel: '12.x'
+ testbench: '^10.11'
+ pest: '^4.0'
+ - php: '8.4'
+ laravel: '13.x'
+ testbench: '^11.1'
+ pest: '^4.0'
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: none
+ tools: composer:v2
+
+ - name: Validate composer.json
+ run: composer validate --strict
+
+ - name: Install dependencies for matrix target
+ run: >
+ composer update
+ --prefer-dist
+ --no-interaction
+ --with-all-dependencies
+ "illuminate/contracts:${{ matrix.laravel }}"
+ "illuminate/http:${{ matrix.laravel }}"
+ "illuminate/support:${{ matrix.laravel }}"
+ "orchestra/testbench:${{ matrix.testbench }}"
+ "pestphp/pest:${{ matrix.pest }}"
+
+ - name: Run Pest
+ run: vendor/bin/pest
+
+ quality:
+ name: Quality
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.4'
+ coverage: none
+ tools: composer:v2
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-interaction
+
+ - name: Run PHPStan
+ run: composer analyse
+
+ - name: Run Pint
+ run: vendor/bin/pint --test
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..ab1f416
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..83879ce
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/laravel-ares.iml b/.idea/laravel-ares.iml
new file mode 100644
index 0000000..07b238f
--- /dev/null
+++ b/.idea/laravel-ares.iml
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/laravel-idea-personal.xml b/.idea/laravel-idea-personal.xml
new file mode 100644
index 0000000..425e121
--- /dev/null
+++ b/.idea/laravel-idea-personal.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/laravel-idea.xml b/.idea/laravel-idea.xml
new file mode 100644
index 0000000..d8e17dd
--- /dev/null
+++ b/.idea/laravel-idea.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..3ce3588
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f00f124
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/php-docker-settings.xml b/.idea/php-docker-settings.xml
new file mode 100644
index 0000000..c3b1223
--- /dev/null
+++ b/.idea/php-docker-settings.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml
new file mode 100644
index 0000000..b97138c
--- /dev/null
+++ b/.idea/php-test-framework.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
new file mode 100644
index 0000000..3ff3c62
--- /dev/null
+++ b/.idea/php.xml
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/stat.log b/.idea/stat.log
new file mode 100644
index 0000000..fd45870
--- /dev/null
+++ b/.idea/stat.log
@@ -0,0 +1,447 @@
+{"ts":1777018029320,"action":"PROJECT_OPENED","tags":{"PROJECT_NAME":"laravel-ares"}}
+{"ts":1777018032010,"action":"IDE_DEACTIVATED"}
+{"ts":1777018032185,"action":"IDE_ACTIVATED"}
+{"ts":1777018034155,"action":"FILE_OPENED","file":"/README.md","tags":{"FILE_LINE_OF_CODE":"3"}}
+{"ts":1777018039572,"action":"IDE_DEACTIVATED"}
+{"ts":1777018044489,"action":"IDE_ACTIVATED"}
+{"ts":1777018044830,"action":"IDE_DEACTIVATED"}
+{"ts":1777018048478,"action":"IDE_ACTIVATED"}
+{"ts":1777018055159,"action":"FILE_CLOSED","file":"/README.md","tags":{"FILE_LINE_OF_CODE":"3"}}
+{"ts":1777018055159,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018055597,"action":"IDE_DEACTIVATED"}
+{"ts":1777018056271,"action":"IDE_ACTIVATED"}
+{"ts":1777018057784,"action":"IDE_DEACTIVATED"}
+{"ts":1777018059693,"action":"IDE_ACTIVATED"}
+{"ts":1777018062595,"action":"IDE_DEACTIVATED"}
+{"ts":1777018063341,"action":"IDE_ACTIVATED"}
+{"ts":1777018064035,"action":"IDE_DEACTIVATED"}
+{"ts":1777018066682,"action":"IDE_ACTIVATED"}
+{"ts":1777018072099,"action":"IDE_DEACTIVATED"}
+{"ts":1777018075130,"action":"IDE_ACTIVATED"}
+{"ts":1777018087547,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"34"}}
+{"ts":1777018087547,"action":"FILE_OPENED","file":"/src/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018089595,"action":"IDE_DEACTIVATED"}
+{"ts":1777018091194,"action":"IDE_ACTIVATED"}
+{"ts":1777018103743,"action":"FILE_CLOSED","file":"/src/Provider/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018103743,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"34"}}
+{"ts":1777018108811,"action":"IDE_DEACTIVATED"}
+{"ts":1777018112010,"action":"IDE_ACTIVATED"}
+{"ts":1777018112973,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"34"}}
+{"ts":1777018112973,"action":"FILE_OPENED","file":"/src/Provider/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018133981,"action":"FILE_CLOSED","file":"/src/Provider/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"44"}}
+{"ts":1777018133981,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"34"}}
+{"ts":1777018230070,"action":"IDE_DEACTIVATED"}
+{"ts":1777018234449,"action":"IDE_ACTIVATED"}
+{"ts":1777018238691,"action":"IDE_DEACTIVATED"}
+{"ts":1777018277380,"action":"IDE_ACTIVATED"}
+{"ts":1777018313501,"action":"IDE_DEACTIVATED"}
+{"ts":1777018316719,"action":"IDE_ACTIVATED"}
+{"ts":1777018321911,"action":"IDE_DEACTIVATED"}
+{"ts":1777018324995,"action":"IDE_ACTIVATED"}
+{"ts":1777018326792,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777018326792,"action":"FILE_OPENED","file":"/src/config/ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018327278,"action":"IDE_DEACTIVATED"}
+{"ts":1777018327283,"action":"IDE_ACTIVATED"}
+{"ts":1777018332045,"action":"IDE_DEACTIVATED"}
+{"ts":1777018332654,"action":"IDE_ACTIVATED"}
+{"ts":1777018333695,"action":"IDE_DEACTIVATED"}
+{"ts":1777018353049,"action":"IDE_ACTIVATED"}
+{"ts":1777018354886,"action":"FILE_CLOSED","file":"/src/config/ares.php","tags":{"FILE_LINE_OF_CODE":"11"}}
+{"ts":1777018354886,"action":"FILE_OPENED","file":"/src/Provider/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"44"}}
+{"ts":1777018373610,"action":"IDE_DEACTIVATED"}
+{"ts":1777018398175,"action":"IDE_ACTIVATED"}
+{"ts":1777018402045,"action":"IDE_DEACTIVATED"}
+{"ts":1777018468113,"action":"IDE_ACTIVATED"}
+{"ts":1777018493116,"action":"FILE_CLOSED","file":"/src/Provider/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"42"}}
+{"ts":1777018493116,"action":"FILE_OPENED","file":"/vendor/fakerphp/faker/src/Faker/Factory.php","tags":{"FILE_LINE_OF_CODE":"72"}}
+{"ts":1777018510219,"action":"FILE_CLOSED","file":"/vendor/fakerphp/faker/src/Faker/Factory.php","tags":{"FILE_LINE_OF_CODE":"72"}}
+{"ts":1777018510219,"action":"FILE_OPENED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"42"}}
+{"ts":1777018528844,"action":"IDE_DEACTIVATED"}
+{"ts":1777018537441,"action":"IDE_ACTIVATED"}
+{"ts":1777018544284,"action":"IDE_DEACTIVATED"}
+{"ts":1777018547850,"action":"IDE_ACTIVATED"}
+{"ts":1777018575642,"action":"IDE_DEACTIVATED"}
+{"ts":1777018577349,"action":"IDE_ACTIVATED"}
+{"ts":1777018580554,"action":"FILE_CLOSED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"53"}}
+{"ts":1777018580554,"action":"FILE_OPENED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018581057,"action":"IDE_DEACTIVATED"}
+{"ts":1777018581843,"action":"IDE_ACTIVATED"}
+{"ts":1777018583075,"action":"IDE_DEACTIVATED"}
+{"ts":1777018584377,"action":"IDE_ACTIVATED"}
+{"ts":1777018585475,"action":"IDE_DEACTIVATED"}
+{"ts":1777018590399,"action":"IDE_ACTIVATED"}
+{"ts":1777018593800,"action":"FILE_CLOSED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018593801,"action":"FILE_OPENED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018594502,"action":"IDE_DEACTIVATED"}
+{"ts":1777018595187,"action":"IDE_ACTIVATED"}
+{"ts":1777018595942,"action":"IDE_DEACTIVATED"}
+{"ts":1777018597489,"action":"IDE_ACTIVATED"}
+{"ts":1777018598855,"action":"IDE_DEACTIVATED"}
+{"ts":1777018603497,"action":"IDE_ACTIVATED"}
+{"ts":1777018607693,"action":"FILE_CLOSED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"101"}}
+{"ts":1777018607693,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018608467,"action":"IDE_DEACTIVATED"}
+{"ts":1777018609274,"action":"IDE_ACTIVATED"}
+{"ts":1777018609915,"action":"IDE_DEACTIVATED"}
+{"ts":1777018611296,"action":"IDE_ACTIVATED"}
+{"ts":1777018613568,"action":"IDE_DEACTIVATED"}
+{"ts":1777018617224,"action":"IDE_ACTIVATED"}
+{"ts":1777018621609,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777018621609,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018624343,"action":"IDE_DEACTIVATED"}
+{"ts":1777018626174,"action":"IDE_ACTIVATED"}
+{"ts":1777018627715,"action":"IDE_DEACTIVATED"}
+{"ts":1777018631477,"action":"IDE_ACTIVATED"}
+{"ts":1777018635080,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777018635080,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018637769,"action":"IDE_DEACTIVATED"}
+{"ts":1777018639204,"action":"IDE_ACTIVATED"}
+{"ts":1777018639484,"action":"IDE_DEACTIVATED"}
+{"ts":1777018640805,"action":"IDE_ACTIVATED"}
+{"ts":1777018644186,"action":"IDE_DEACTIVATED"}
+{"ts":1777018644855,"action":"IDE_ACTIVATED"}
+{"ts":1777018649634,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"20"}}
+{"ts":1777018649634,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777018651060,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777018651060,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777018654780,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777018654780,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777018662227,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777018662227,"action":"FILE_OPENED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"53"}}
+{"ts":1777018675160,"action":"FILE_CLOSED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"53"}}
+{"ts":1777018675160,"action":"FILE_OPENED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018677271,"action":"FILE_CLOSED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018677271,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777018678077,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777018678077,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777018678813,"action":"IDE_DEACTIVATED"}
+{"ts":1777018689917,"action":"IDE_ACTIVATED"}
+{"ts":1777018695169,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777018695169,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018695743,"action":"IDE_DEACTIVATED"}
+{"ts":1777018696614,"action":"IDE_ACTIVATED"}
+{"ts":1777018697630,"action":"IDE_DEACTIVATED"}
+{"ts":1777018701656,"action":"IDE_ACTIVATED"}
+{"ts":1777018703356,"action":"IDE_DEACTIVATED"}
+{"ts":1777018711636,"action":"IDE_ACTIVATED"}
+{"ts":1777018715930,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"38"}}
+{"ts":1777018715930,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php ","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018717197,"action":"IDE_DEACTIVATED"}
+{"ts":1777018718847,"action":"IDE_ACTIVATED"}
+{"ts":1777018719253,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"38"}}
+{"ts":1777018725076,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"46"}}
+{"ts":1777018725076,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018726256,"action":"IDE_DEACTIVATED"}
+{"ts":1777018728892,"action":"IDE_ACTIVATED"}
+{"ts":1777018731951,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"9"}}
+{"ts":1777018731951,"action":"FILE_OPENED","file":"/resources/lang/en/ares.php ","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018732580,"action":"IDE_DEACTIVATED"}
+{"ts":1777018734048,"action":"IDE_ACTIVATED"}
+{"ts":1777018734426,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"9"}}
+{"ts":1777018736787,"action":"IDE_DEACTIVATED"}
+{"ts":1777018743456,"action":"IDE_ACTIVATED"}
+{"ts":1777018746932,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"9"}}
+{"ts":1777018746932,"action":"FILE_OPENED","file":"/example/blade-usage.blade.php ","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018747587,"action":"IDE_DEACTIVATED"}
+{"ts":1777018749041,"action":"IDE_ACTIVATED"}
+{"ts":1777018749485,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777018752139,"action":"IDE_DEACTIVATED"}
+{"ts":1777018756329,"action":"IDE_ACTIVATED"}
+{"ts":1777018759544,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018759544,"action":"FILE_OPENED","file":"/example/example/livewire-usage.php ","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018760352,"action":"IDE_DEACTIVATED"}
+{"ts":1777018761965,"action":"IDE_ACTIVATED"}
+{"ts":1777018762428,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018765012,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018765012,"action":"FILE_OPENED","file":"/example/blade-usage.blade.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018767029,"action":"FILE_CLOSED","file":"/example/blade-usage.blade.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018767029,"action":"FILE_OPENED","file":"/example/example/livewire-usage.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018774372,"action":"FILE_CLOSED","file":"/example/livewire-usage.php","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777018774372,"action":"FILE_OPENED","file":"/example/blade-usage.blade.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018777450,"action":"IDE_DEACTIVATED"}
+{"ts":1777018783268,"action":"IDE_ACTIVATED"}
+{"ts":1777018788882,"action":"IDE_DEACTIVATED"}
+{"ts":1777018885223,"action":"IDE_ACTIVATED"}
+{"ts":1777018887078,"action":"FILE_CLOSED","file":"/example/blade-usage.blade.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018887078,"action":"FILE_OPENED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018892017,"action":"FILE_CLOSED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018892017,"action":"FILE_OPENED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"53"}}
+{"ts":1777018895811,"action":"IDE_DEACTIVATED"}
+{"ts":1777018905336,"action":"IDE_ACTIVATED"}
+{"ts":1777018911802,"action":"FILE_CLOSED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"53"}}
+{"ts":1777018911802,"action":"FILE_OPENED","file":"/config/ares.php","tags":{"FILE_LINE_OF_CODE":"11"}}
+{"ts":1777018913619,"action":"IDE_DEACTIVATED"}
+{"ts":1777018928890,"action":"IDE_ACTIVATED"}
+{"ts":1777018934962,"action":"IDE_DEACTIVATED"}
+{"ts":1777018936602,"action":"IDE_ACTIVATED"}
+{"ts":1777018940235,"action":"FILE_CLOSED","file":"/config/ares.php","tags":{"FILE_LINE_OF_CODE":"34"}}
+{"ts":1777018940235,"action":"FILE_OPENED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777018944442,"action":"IDE_DEACTIVATED"}
+{"ts":1777018946405,"action":"IDE_ACTIVATED"}
+{"ts":1777018949743,"action":"IDE_DEACTIVATED"}
+{"ts":1777018952199,"action":"IDE_ACTIVATED"}
+{"ts":1777018953302,"action":"FILE_CLOSED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777018953302,"action":"FILE_OPENED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018955196,"action":"IDE_DEACTIVATED"}
+{"ts":1777018957889,"action":"IDE_ACTIVATED"}
+{"ts":1777018959648,"action":"IDE_DEACTIVATED"}
+{"ts":1777018962837,"action":"IDE_ACTIVATED"}
+{"ts":1777018968102,"action":"IDE_DEACTIVATED"}
+{"ts":1777018972680,"action":"IDE_ACTIVATED"}
+{"ts":1777018978538,"action":"FILE_CLOSED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777018978538,"action":"FILE_OPENED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"101"}}
+{"ts":1777018979598,"action":"IDE_DEACTIVATED"}
+{"ts":1777018981508,"action":"IDE_ACTIVATED"}
+{"ts":1777018987646,"action":"IDE_DEACTIVATED"}
+{"ts":1777018994079,"action":"IDE_ACTIVATED"}
+{"ts":1777019000135,"action":"FILE_CLOSED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"100"}}
+{"ts":1777019000135,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777019000660,"action":"IDE_DEACTIVATED"}
+{"ts":1777019002610,"action":"IDE_ACTIVATED"}
+{"ts":1777019008266,"action":"IDE_DEACTIVATED"}
+{"ts":1777019009959,"action":"IDE_ACTIVATED"}
+{"ts":1777019012717,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019012717,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019014821,"action":"IDE_DEACTIVATED"}
+{"ts":1777019015849,"action":"IDE_ACTIVATED"}
+{"ts":1777019019095,"action":"IDE_DEACTIVATED"}
+{"ts":1777019031131,"action":"IDE_ACTIVATED"}
+{"ts":1777019033645,"action":"IDE_DEACTIVATED"}
+{"ts":1777019036567,"action":"IDE_ACTIVATED"}
+{"ts":1777019037786,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019037786,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019041693,"action":"IDE_DEACTIVATED"}
+{"ts":1777019045798,"action":"IDE_ACTIVATED"}
+{"ts":1777019047669,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777019047669,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"20"}}
+{"ts":1777019051702,"action":"IDE_DEACTIVATED"}
+{"ts":1777019056534,"action":"IDE_ACTIVATED"}
+{"ts":1777019057843,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"20"}}
+{"ts":1777019057843,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"46"}}
+{"ts":1777019058363,"action":"IDE_DEACTIVATED"}
+{"ts":1777019060044,"action":"IDE_ACTIVATED"}
+{"ts":1777019062135,"action":"IDE_DEACTIVATED"}
+{"ts":1777019074832,"action":"IDE_ACTIVATED"}
+{"ts":1777019076210,"action":"IDE_DEACTIVATED"}
+{"ts":1777019077884,"action":"IDE_ACTIVATED"}
+{"ts":1777019079969,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"38"}}
+{"ts":1777019079969,"action":"FILE_OPENED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777019081165,"action":"FILE_CLOSED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777019081165,"action":"FILE_OPENED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019081955,"action":"FILE_CLOSED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019081955,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777019082461,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777019082461,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019083105,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019083105,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"20"}}
+{"ts":1777019090412,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"20"}}
+{"ts":1777019090412,"action":"FILE_OPENED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777019092258,"action":"FILE_CLOSED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777019092258,"action":"FILE_OPENED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"53"}}
+{"ts":1777019108994,"action":"FILE_CLOSED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"54"}}
+{"ts":1777019108994,"action":"FILE_OPENED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"100"}}
+{"ts":1777019170014,"action":"FILE_CLOSED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"101"}}
+{"ts":1777019170014,"action":"FILE_OPENED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019171494,"action":"FILE_CLOSED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019171494,"action":"FILE_OPENED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"101"}}
+{"ts":1777019196556,"action":"FILE_CLOSED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"101"}}
+{"ts":1777019196556,"action":"FILE_OPENED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"54"}}
+{"ts":1777019197206,"action":"FILE_CLOSED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"54"}}
+{"ts":1777019197206,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"20"}}
+{"ts":1777019214401,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"20"}}
+{"ts":1777019214401,"action":"FILE_OPENED","file":"/example/blade-usage.blade.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019216811,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019232776,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019232776,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"38"}}
+{"ts":1777019234725,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"38"}}
+{"ts":1777019234725,"action":"FILE_OPENED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777019236095,"action":"FILE_CLOSED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777019236095,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"33"}}
+{"ts":1777019236789,"action":"IDE_DEACTIVATED"}
+{"ts":1777019244155,"action":"IDE_ACTIVATED"}
+{"ts":1777019247907,"action":"IDE_DEACTIVATED"}
+{"ts":1777019250012,"action":"IDE_ACTIVATED"}
+{"ts":1777019251287,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"9"}}
+{"ts":1777019251287,"action":"FILE_OPENED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"0"}}
+{"ts":1777019254475,"action":"FILE_CLOSED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"9"}}
+{"ts":1777019254475,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"38"}}
+{"ts":1777019256444,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"38"}}
+{"ts":1777019256444,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019262571,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777019262571,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777019269464,"action":"IDE_DEACTIVATED"}
+{"ts":1777019283530,"action":"IDE_ACTIVATED"}
+{"ts":1777019293237,"action":"PROJECT_CLOSED","tags":{"PROJECT_NAME":"laravel-ares"}}
+{"ts":1777019384663,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777019387912,"action":"PROJECT_OPENED","tags":{"PROJECT_NAME":"laravel-ares"}}
+{"ts":1777019487056,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777019487056,"action":"FILE_OPENED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"101"}}
+{"ts":1777019536838,"action":"FILE_CLOSED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"101"}}
+{"ts":1777019536838,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777019565536,"action":"IDE_DEACTIVATED"}
+{"ts":1777019565624,"action":"IDE_ACTIVATED"}
+{"ts":1777019612835,"action":"IDE_DEACTIVATED"}
+{"ts":1777020270882,"action":"IDE_ACTIVATED"}
+{"ts":1777020278745,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"56"}}
+{"ts":1777020332358,"action":"FILE_OPENED","file":"/README.md","tags":{"FILE_LINE_OF_CODE":"175"}}
+{"ts":1777020368122,"action":"FILE_CLOSED","file":"/README.md","tags":{"FILE_LINE_OF_CODE":"175"}}
+{"ts":1777020368122,"action":"FILE_OPENED","file":"/tests/Pest.php","tags":{"FILE_LINE_OF_CODE":"8"}}
+{"ts":1777020373748,"action":"FILE_CLOSED","file":"/tests/Pest.php","tags":{"FILE_LINE_OF_CODE":"8"}}
+{"ts":1777020373748,"action":"FILE_OPENED","file":"/tests/Unit/CompanyDataTest.php","tags":{"FILE_LINE_OF_CODE":"36"}}
+{"ts":1777020380970,"action":"FILE_CLOSED","file":"/tests/Unit/CompanyDataTest.php","tags":{"FILE_LINE_OF_CODE":"36"}}
+{"ts":1777020380970,"action":"FILE_OPENED","file":"/tests/Feature/AresClientTest.php","tags":{"FILE_LINE_OF_CODE":"100"}}
+{"ts":1777020419518,"action":"FILE_CLOSED","file":"/tests/Feature/AresClientTest.php","tags":{"FILE_LINE_OF_CODE":"100"}}
+{"ts":1777020419518,"action":"FILE_OPENED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"114"}}
+{"ts":1777020421766,"action":"FILE_CLOSED","file":"/src/Services/AresClient.php","tags":{"FILE_LINE_OF_CODE":"114"}}
+{"ts":1777020421766,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777020442659,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777020442659,"action":"FILE_OPENED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"15"}}
+{"ts":1777020444334,"action":"FILE_CLOSED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"15"}}
+{"ts":1777020444334,"action":"FILE_OPENED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"34"}}
+{"ts":1777020445520,"action":"FILE_CLOSED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"34"}}
+{"ts":1777020445520,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020457495,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020457495,"action":"FILE_OPENED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020463463,"action":"FILE_CLOSED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020463463,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020470814,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020470814,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777020475221,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"41"}}
+{"ts":1777020475221,"action":"FILE_OPENED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"61"}}
+{"ts":1777020533935,"action":"IDE_DEACTIVATED"}
+{"ts":1777020640646,"action":"IDE_ACTIVATED"}
+{"ts":1777020667642,"action":"IDE_DEACTIVATED"}
+{"ts":1777020669050,"action":"IDE_ACTIVATED"}
+{"ts":1777020670702,"action":"IDE_DEACTIVATED"}
+{"ts":1777020673142,"action":"IDE_ACTIVATED"}
+{"ts":1777020675969,"action":"IDE_DEACTIVATED"}
+{"ts":1777020677239,"action":"IDE_ACTIVATED"}
+{"ts":1777020736932,"action":"IDE_DEACTIVATED"}
+{"ts":1777020866482,"action":"IDE_ACTIVATED"}
+{"ts":1777020869630,"action":"FILE_CLOSED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"63"}}
+{"ts":1777020902352,"action":"FILE_OPENED","file":"/tests/Pest.php","tags":{"FILE_LINE_OF_CODE":"8"}}
+{"ts":1777020903836,"action":"FILE_CLOSED","file":"/tests/Pest.php","tags":{"FILE_LINE_OF_CODE":"8"}}
+{"ts":1777020903836,"action":"FILE_OPENED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020904961,"action":"FILE_CLOSED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020904961,"action":"FILE_OPENED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020908392,"action":"FILE_CLOSED","file":"/resources/lang/cs/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777020908392,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"47"}}
+{"ts":1777020933493,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"47"}}
+{"ts":1777020943119,"action":"IDE_DEACTIVATED"}
+{"ts":1777020944410,"action":"IDE_ACTIVATED"}
+{"ts":1777020950020,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"56"}}
+{"ts":1777020961763,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"56"}}
+{"ts":1777020961763,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"21"}}
+{"ts":1777021032651,"action":"IDE_DEACTIVATED"}
+{"ts":1777021267247,"action":"IDE_ACTIVATED"}
+{"ts":1777021271933,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"27"}}
+{"ts":1777021332150,"action":"IDE_DEACTIVATED"}
+{"ts":1777021859583,"action":"IDE_ACTIVATED"}
+{"ts":1777021930823,"action":"IDE_DEACTIVATED"}
+{"ts":1777021968339,"action":"IDE_ACTIVATED"}
+{"ts":1777022056835,"action":"IDE_DEACTIVATED"}
+{"ts":1777022165233,"action":"IDE_ACTIVATED"}
+{"ts":1777022201040,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"27"}}
+{"ts":1777022240302,"action":"IDE_DEACTIVATED"}
+{"ts":1777022495899,"action":"IDE_ACTIVATED"}
+{"ts":1777022501167,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"27"}}
+{"ts":1777022501167,"action":"FILE_OPENED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"138"}}
+{"ts":1777022502238,"action":"FILE_CLOSED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"138"}}
+{"ts":1777022502238,"action":"FILE_OPENED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"75"}}
+{"ts":1777022514264,"action":"FILE_CLOSED","file":"/src/Data/CompanyData.php","tags":{"FILE_LINE_OF_CODE":"75"}}
+{"ts":1777022514264,"action":"FILE_OPENED","file":"/src/Data/DeliveryAddressData.php","tags":{"FILE_LINE_OF_CODE":"49"}}
+{"ts":1777022518905,"action":"FILE_CLOSED","file":"/src/Data/DeliveryAddressData.php","tags":{"FILE_LINE_OF_CODE":"49"}}
+{"ts":1777022518905,"action":"FILE_OPENED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"138"}}
+{"ts":1777022524333,"action":"FILE_CLOSED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"138"}}
+{"ts":1777022524333,"action":"FILE_OPENED","file":"/phpstan.neon.dist","tags":{"FILE_LINE_OF_CODE":"19"}}
+{"ts":1777022558823,"action":"IDE_DEACTIVATED"}
+{"ts":1777022578297,"action":"IDE_ACTIVATED"}
+{"ts":1777022595706,"action":"IDE_DEACTIVATED"}
+{"ts":1777022643025,"action":"IDE_ACTIVATED"}
+{"ts":1777022694803,"action":"IDE_DEACTIVATED"}
+{"ts":1777022798895,"action":"IDE_ACTIVATED"}
+{"ts":1777023046053,"action":"IDE_DEACTIVATED"}
+{"ts":1777023264942,"action":"IDE_ACTIVATED"}
+{"ts":1777023305628,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"27"}}
+{"ts":1777023576283,"action":"IDE_DEACTIVATED"}
+{"ts":1777023576357,"action":"IDE_ACTIVATED"}
+{"ts":1777023600030,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"27"}}
+{"ts":1777023600030,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"61"}}
+{"ts":1777023637317,"action":"IDE_DEACTIVATED"}
+{"ts":1777023650285,"action":"IDE_ACTIVATED"}
+{"ts":1777023656612,"action":"IDE_DEACTIVATED"}
+{"ts":1777023660580,"action":"IDE_ACTIVATED"}
+{"ts":1777023666262,"action":"IDE_DEACTIVATED"}
+{"ts":1777023667798,"action":"IDE_ACTIVATED"}
+{"ts":1777023699997,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"61"}}
+{"ts":1777023705701,"action":"FILE_OPENED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"61"}}
+{"ts":1777023780977,"action":"IDE_DEACTIVATED"}
+{"ts":1777023783317,"action":"IDE_ACTIVATED"}
+{"ts":1777023789748,"action":"IDE_DEACTIVATED"}
+{"ts":1777023789826,"action":"IDE_ACTIVATED"}
+{"ts":1777023813462,"action":"FILE_CLOSED","file":"/composer.json","tags":{"FILE_LINE_OF_CODE":"61"}}
+{"ts":1777023813462,"action":"FILE_OPENED","file":"/phpstan.neon.dist","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777023825527,"action":"FILE_CLOSED","file":"/phpstan.neon.dist","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777023825527,"action":"FILE_OPENED","file":"/.phpstan/resultCache.php","tags":{"FILE_LINE_OF_CODE":"4584"}}
+{"ts":1777023830247,"action":"FILE_CLOSED","file":"/.phpstan/resultCache.php","tags":{"FILE_LINE_OF_CODE":"4584"}}
+{"ts":1777023830247,"action":"FILE_OPENED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777023832022,"action":"FILE_CLOSED","file":"/resources/lang/en/ares.php","tags":{"FILE_LINE_OF_CODE":"10"}}
+{"ts":1777023832022,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"54"}}
+{"ts":1777023833639,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"54"}}
+{"ts":1777023833639,"action":"FILE_OPENED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"26"}}
+{"ts":1777023834669,"action":"FILE_CLOSED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"26"}}
+{"ts":1777023834669,"action":"FILE_OPENED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"139"}}
+{"ts":1777023835307,"action":"FILE_CLOSED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"139"}}
+{"ts":1777023835307,"action":"FILE_OPENED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"26"}}
+{"ts":1777023836084,"action":"FILE_CLOSED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"26"}}
+{"ts":1777023836084,"action":"FILE_OPENED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"54"}}
+{"ts":1777023837237,"action":"FILE_CLOSED","file":"/src/Commands/TestAresCommand.php","tags":{"FILE_LINE_OF_CODE":"54"}}
+{"ts":1777023837237,"action":"FILE_OPENED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"26"}}
+{"ts":1777023837800,"action":"FILE_CLOSED","file":"/src/Contracts/AresClientInterface.php","tags":{"FILE_LINE_OF_CODE":"26"}}
+{"ts":1777023837800,"action":"FILE_OPENED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"139"}}
+{"ts":1777023846277,"action":"FILE_CLOSED","file":"/src/Data/AddressData.php","tags":{"FILE_LINE_OF_CODE":"139"}}
+{"ts":1777023846278,"action":"FILE_OPENED","file":"/src/Enums/RegistrationSourceState.php","tags":{"FILE_LINE_OF_CODE":"18"}}
+{"ts":1777023847379,"action":"FILE_CLOSED","file":"/src/Enums/RegistrationSourceState.php","tags":{"FILE_LINE_OF_CODE":"18"}}
+{"ts":1777023847379,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"17"}}
+{"ts":1777023847694,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupFailed.php","tags":{"FILE_LINE_OF_CODE":"17"}}
+{"ts":1777023847694,"action":"FILE_OPENED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777023848765,"action":"FILE_CLOSED","file":"/src/Events/CompanyLookupSucceeded.php","tags":{"FILE_LINE_OF_CODE":"13"}}
+{"ts":1777023848765,"action":"FILE_OPENED","file":"/src/Exceptions/CompanyNotFoundException.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777023849079,"action":"FILE_CLOSED","file":"/src/Exceptions/CompanyNotFoundException.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777023849079,"action":"FILE_OPENED","file":"/src/Exceptions/InvalidApiResponseException.php","tags":{"FILE_LINE_OF_CODE":"21"}}
+{"ts":1777023849314,"action":"FILE_CLOSED","file":"/src/Exceptions/InvalidApiResponseException.php","tags":{"FILE_LINE_OF_CODE":"21"}}
+{"ts":1777023849314,"action":"FILE_OPENED","file":"/src/Exceptions/InvalidIcException.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777023850081,"action":"FILE_CLOSED","file":"/src/Exceptions/InvalidIcException.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777023850081,"action":"FILE_OPENED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"27"}}
+{"ts":1777023850796,"action":"FILE_CLOSED","file":"/src/Facades/Ares.php","tags":{"FILE_LINE_OF_CODE":"27"}}
+{"ts":1777023850796,"action":"FILE_OPENED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"84"}}
+{"ts":1777023852737,"action":"FILE_CLOSED","file":"/src/Providers/AresServiceProvider.php","tags":{"FILE_LINE_OF_CODE":"84"}}
+{"ts":1777023852737,"action":"FILE_OPENED","file":"/src/Exceptions/InvalidIcException.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777023860757,"action":"FILE_CLOSED","file":"/src/Exceptions/InvalidIcException.php","tags":{"FILE_LINE_OF_CODE":"16"}}
+{"ts":1777023860757,"action":"FILE_OPENED","file":"/tests/Feature/AresClientTest.php","tags":{"FILE_LINE_OF_CODE":"244"}}
+{"ts":1777023861028,"action":"FILE_CLOSED","file":"/tests/Feature/AresClientTest.php","tags":{"FILE_LINE_OF_CODE":"244"}}
+{"ts":1777023861028,"action":"FILE_OPENED","file":"/tests/Feature/TestAresCommandTest.php","tags":{"FILE_LINE_OF_CODE":"140"}}
+{"ts":1777023861371,"action":"FILE_CLOSED","file":"/tests/Feature/TestAresCommandTest.php","tags":{"FILE_LINE_OF_CODE":"140"}}
+{"ts":1777023861371,"action":"FILE_OPENED","file":"/tests/Pest.php","tags":{"FILE_LINE_OF_CODE":"8"}}
+{"ts":1777023862214,"action":"FILE_CLOSED","file":"/tests/Pest.php","tags":{"FILE_LINE_OF_CODE":"8"}}
+{"ts":1777023862214,"action":"FILE_OPENED","file":"/tests/Unit/CompanyDataTest.php","tags":{"FILE_LINE_OF_CODE":"116"}}
+{"ts":1777023879329,"action":"FILE_CLOSED","file":"/tests/Unit/CompanyDataTest.php","tags":{"FILE_LINE_OF_CODE":"116"}}
+{"ts":1777023879329,"action":"FILE_OPENED","file":"/README.md","tags":{"FILE_LINE_OF_CODE":"177"}}
+{"ts":1777023893214,"action":"IDE_DEACTIVATED"}
+{"ts":1777023893283,"action":"IDE_ACTIVATED"}
+{"ts":1777023912506,"action":"IDE_DEACTIVATED"}
+{"ts":1777023959432,"action":"IDE_ACTIVATED"}
+{"ts":1777023964248,"action":"IDE_DEACTIVATED"}
+{"ts":1777023977962,"action":"IDE_ACTIVATED"}
+{"ts":1777023990966,"action":"IDE_DEACTIVATED"}
+{"ts":1777024003962,"action":"IDE_ACTIVATED"}
+{"ts":1777024031574,"action":"IDE_DEACTIVATED"}
+{"ts":1777024031653,"action":"IDE_ACTIVATED"}
+{"ts":1777024244369,"action":"IDE_DEACTIVATED"}
+{"ts":1777024244737,"action":"IDE_ACTIVATED"}
+{"ts":1777024281303,"action":"FILE_CLOSED","file":"/README.md","tags":{"FILE_LINE_OF_CODE":"177"}}
+{"ts":1777024281303,"action":"FILE_OPENED","file":"/tests/Unit/ServiceProviderTest.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777024281827,"action":"FILE_CLOSED","file":"/tests/Unit/ServiceProviderTest.php","tags":{"FILE_LINE_OF_CODE":"12"}}
+{"ts":1777024281827,"action":"FILE_OPENED","file":"/tests/Unit/CompanyDataTest.php","tags":{"FILE_LINE_OF_CODE":"116"}}
+{"ts":1777024292789,"action":"FILE_CLOSED","file":"/tests/Unit/CompanyDataTest.php","tags":{"FILE_LINE_OF_CODE":"116"}}
+{"ts":1777024292789,"action":"FILE_OPENED","file":"/.github/workflows/ci.yml","tags":{"FILE_LINE_OF_CODE":"88"}}
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 92de2b8..c9bbfff 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,176 @@
# laravel-ares
-Laravel package for ARES (Czech business register) API
+
+`laravel-ares` is a Laravel package for the Czech ARES business register API. It provides a typed client, a facade, configurable caching, lookup events, ICO validation, static analysis support, and an artisan command for diagnostics.
+
+## Features
+
+- Typed public API through `AresClientInterface`
+- `Ares` facade with convenience methods for common workflows
+- Structured domain objects instead of one large flat payload object
+- Configurable caching and HTTP timeouts
+- Events for successful and failed lookups
+- ICO normalization and checksum validation
+- Explicit exceptions for invalid ICO and missing companies
+- Pest test suite, PHPStan configuration, and GitHub Actions CI
+
+## Requirements
+
+- PHP 8.1+
+- Laravel 10, 11, 12, or 13
+
+## Installation
+
+Install the package with Composer:
+
+```bash
+composer require nyoncode/laravel-ares
+```
+
+Publish the configuration file if you want local overrides:
+
+```bash
+php artisan vendor:publish --tag=laravel-ares::config
+```
+
+## Configuration
+
+| Key | Default | Description |
+| --- | --- | --- |
+| `api_url` | `https://ares.gov.cz/ekonomicke-subjekty-v-be/rest` | Base URL for the ARES REST API |
+| `cache_ttl` | `86400` | Cache lifetime for successful lookups in seconds |
+| `log_channel` | `stack` | Laravel log channel used for client errors |
+| `http_options.timeout` | `5.0` | Request timeout in seconds |
+| `http_options.connect_timeout` | `3.0` | Connection timeout in seconds |
+
+## Usage
+
+Use dependency injection when you want explicit contracts:
+
+```php
+use NyonCode\Ares\Contracts\AresClientInterface;
+
+final class CompanyLookupService
+{
+ public function __construct(
+ private readonly AresClientInterface $ares,
+ ) {}
+
+ public function companyName(string $ic): ?string
+ {
+ return $this->ares->findCompany($ic)?->name;
+ }
+}
+```
+
+Use the facade for concise application code:
+
+```php
+use NyonCode\Ares\Facades\Ares;
+
+$normalizedIc = Ares::normalizeIc('27 074 358');
+$company = Ares::findCompanyOrFail($normalizedIc);
+
+dump($company->name);
+dump($company->registeredOffice?->formatted);
+dump($company->registration->naceCodes);
+```
+
+Public API:
+
+- `findCompany(string $ic): ?CompanyData`
+- `findCompanyRaw(string $ic): ?array`
+- `findCompanyOrFail(string $ic): CompanyData`
+- `forgetCompany(string $ic): bool`
+- `isValidIc(string $ic): bool`
+- `normalizeIc(string $ic): string`
+
+## Domain Model
+
+Successful lookups return `NyonCode\Ares\Data\CompanyData`:
+
+```php
+final class CompanyData
+{
+ public readonly string $ic;
+ public readonly string $name;
+ public readonly ?string $dic;
+ public readonly ?string $dicSkDph;
+ public readonly ?AddressData $registeredOffice;
+ public readonly ?DeliveryAddressData $deliveryAddress;
+ public readonly RegistrationData $registration;
+ public readonly array $rawData;
+}
+```
+
+Related DTOs:
+
+- `AddressData` models the registered office
+- `DeliveryAddressData` models the mailing address
+- `RegistrationData` groups legal form, dates, source, file mark, NACE codes, and source statuses
+- `RegistrationStatusData` represents one registry source status
+- `RegistrationSourceState` is a typed enum for known ARES status values
+
+`rawData` remains available as an escape hatch for fields the package does not map yet.
+
+## Exceptions
+
+The fail-fast API throws explicit domain exceptions:
+
+- `NyonCode\Ares\Exceptions\InvalidIcException`
+- `NyonCode\Ares\Exceptions\CompanyNotFoundException`
+
+Malformed payloads are treated as failed lookups internally and surface through the failure event path.
+
+## Events
+
+The package dispatches:
+
+- `NyonCode\Ares\Events\CompanyLookupSucceeded`
+- `NyonCode\Ares\Events\CompanyLookupFailed`
+
+## Artisan Command
+
+The package includes an artisan helper for manual verification:
+
+```bash
+php artisan ares:test 27074358
+```
+
+The command renders a compact company summary including DIC, source, dates, registered office, delivery address, and register metadata.
+
+## Quality Gates
+
+Run the automated tests:
+
+```bash
+composer test
+```
+
+Run static analysis:
+
+```bash
+composer analyse
+```
+
+Run the formatter:
+
+```bash
+composer format
+```
+
+The repository includes a GitHub Actions workflow for:
+
+- PHP/Laravel compatibility matrix tests
+- PHPStan on the quality lane
+- Pint on the quality lane
+
+## Development Notes
+
+- Successful lookups are cached under the `ares:company:{ic}` key format.
+- Invalid ICO values are rejected before any HTTP request is sent.
+- `forgetCompany()` invalidates cache entries using normalized ICO values.
+- Failed HTTP responses, malformed payloads, and transport exceptions all dispatch `CompanyLookupFailed`.
+
+## License
+
+The package is open-sourced under the [MIT license](LICENSE).
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..cb80a35
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,60 @@
+{
+ "name": "nyoncode/laravel-ares",
+ "description": "Laravel package for the Czech ARES business register API with caching, events, validation and artisan tooling.",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "NyonCode",
+ "email": "info@nyoncode.com"
+ }
+ ],
+ "require": {
+ "php": "^8.1",
+ "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0",
+ "illuminate/http": "^10.0|^11.0|^12.0|^13.0",
+ "illuminate/support": "^10.0|^11.0|^12.0|^13.0",
+ "guzzlehttp/guzzle": "^7.0",
+ "nyoncode/laravel-package-toolkit": "^2.0"
+ },
+ "require-dev": {
+ "larastan/larastan": "^2.9|^3.0",
+ "laravel/pint": "^1.13",
+ "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0",
+ "pestphp/pest": "^2.0|^3.0|^4.0",
+ "phpstan/phpstan": "^2.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "NyonCode\\Ares\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "NyonCode\\Ares\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "analyse": "vendor/bin/phpstan analyse -c phpstan.neon.dist --memory-limit=1G",
+ "test": "vendor/bin/pest",
+ "test:parallel": "vendor/bin/pest --parallel",
+ "format": "vendor/bin/pint"
+ },
+ "extra": {
+ "laravel": {
+ "providers": [
+ "NyonCode\\Ares\\Providers\\AresServiceProvider"
+ ],
+ "aliases": {
+ "Ares": "NyonCode\\Ares\\Facades\\Ares"
+ }
+ }
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "allow-plugins": {
+ "pestphp/pest-plugin": true
+ }
+ }
+}
diff --git a/config/ares.php b/config/ares.php
new file mode 100644
index 0000000..ef2f512
--- /dev/null
+++ b/config/ares.php
@@ -0,0 +1,34 @@
+ env('ARES_API_URL', 'https://ares.gov.cz/ekonomicke-subjekty-v-be/rest'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Cache TTL (seconds)
+ |--------------------------------------------------------------------------
+ */
+ 'cache_ttl' => env('ARES_CACHE_TTL', 86400),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Logging channel
+ |--------------------------------------------------------------------------
+ */
+ 'log_channel' => env('ARES_LOG_CHANNEL', 'stack'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Guzzle HTTP options
+ |--------------------------------------------------------------------------
+ */
+ 'http_options' => [
+ 'timeout' => 5.0,
+ 'connect_timeout' => 3.0,
+ ],
+];
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 0000000..0ebc249
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,12 @@
+includes:
+ - vendor/larastan/larastan/extension.neon
+
+parameters:
+ paths:
+ - src
+ level: max
+ tmpDir: .phpstan
+ treatPhpDocTypesAsCertain: true
+
+ universalObjectCratesClasses:
+ - Illuminate\Container\Container
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..2bd0059
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,21 @@
+
+
+
+
+ tests/Unit
+
+
+ tests/Feature
+
+
+
+
+
+
+
+
diff --git a/resources/lang/cs/ares.php b/resources/lang/cs/ares.php
new file mode 100644
index 0000000..bd2812c
--- /dev/null
+++ b/resources/lang/cs/ares.php
@@ -0,0 +1,9 @@
+ [
+ 'not_found' => "Subjekt s I\u{010C}O :ic nebyl nalezen.",
+ 'api_error' => 'Chyba komunikace s ARES API.',
+ 'invalid_ic' => "Neplatn\u{00E9} I\u{010C}O.",
+ ],
+];
diff --git a/resources/lang/en/ares.php b/resources/lang/en/ares.php
new file mode 100644
index 0000000..9c5b5f2
--- /dev/null
+++ b/resources/lang/en/ares.php
@@ -0,0 +1,9 @@
+ [
+ 'not_found' => 'Entity with IC :ic was not found.',
+ 'api_error' => 'ARES API communication error.',
+ 'invalid_ic' => 'Invalid IC.',
+ ],
+];
diff --git a/src/Commands/TestAresCommand.php b/src/Commands/TestAresCommand.php
new file mode 100644
index 0000000..776c8ae
--- /dev/null
+++ b/src/Commands/TestAresCommand.php
@@ -0,0 +1,53 @@
+icArgument();
+ $company = Ares::findCompany($ic);
+
+ if (! $company) {
+ $this->error(__('laravel-ares::ares.errors.not_found', ['ic' => $ic]));
+
+ return self::FAILURE;
+ }
+
+ $this->info('Company found:');
+ $this->table(
+ ['Property', 'Value'],
+ [
+ ['IC', $company->ic],
+ ['Name', $company->name],
+ ['DIC', $company->dic ?? 'N/A'],
+ ['Primary Source', $company->registration->primarySource ?? 'N/A'],
+ ['Date of Establishment', $company->registration->dateOfEstablishment ?? 'N/A'],
+ ['Financial Office', $company->registration->financialOffice ?? 'N/A'],
+ ['Address', $company->registeredOffice === null ? 'N/A' : ($company->registeredOffice->formatted ?? 'N/A')],
+ ['Delivery Address', $company->deliveryAddress === null ? 'N/A' : ($company->deliveryAddress->formatted ?? 'N/A')],
+ ['Legal Form', $company->registration->legalForm ?? 'N/A'],
+ ['Business Register File Mark', $company->registration->businessRegisterFileMark ?? 'N/A'],
+ ]
+ );
+
+ return self::SUCCESS;
+ }
+
+ private function icArgument(): string
+ {
+ $value = $this->argument('ic');
+
+ return is_scalar($value) ? (string) $value : '';
+ }
+}
diff --git a/src/Contracts/AresClientInterface.php b/src/Contracts/AresClientInterface.php
new file mode 100644
index 0000000..ed1d04a
--- /dev/null
+++ b/src/Contracts/AresClientInterface.php
@@ -0,0 +1,25 @@
+|null
+ */
+ public function findCompanyRaw(string $ic): ?array;
+
+ public function findCompanyOrFail(string $ic): CompanyData;
+
+ public function forgetCompany(string $ic): bool;
+
+ public function isValidIc(string $ic): bool;
+
+ public function normalizeIc(string $ic): string;
+}
diff --git a/src/Data/AddressData.php b/src/Data/AddressData.php
new file mode 100644
index 0000000..1f29b3f
--- /dev/null
+++ b/src/Data/AddressData.php
@@ -0,0 +1,138 @@
+ $data
+ */
+ public static function fromApiResponse(array $data): ?self
+ {
+ $street = self::nullableString($data['nazevUlice'] ?? null);
+ $houseNumber = self::resolveHouseNumber($data);
+ $district = self::nullableString($data['nazevCastiObce'] ?? null);
+ $city = self::nullableString($data['nazevObce'] ?? $data['obec'] ?? null);
+ $postalCode = self::scalarToString($data['psc'] ?? null);
+ $countryCode = self::nullableString($data['kodStatu'] ?? null);
+ $country = self::nullableString($data['nazevStatu'] ?? null);
+ $formatted = self::resolveFormattedAddress($data, $street, $houseNumber, $district, $city, $postalCode);
+
+ if (
+ $formatted === null
+ && $street === null
+ && $houseNumber === null
+ && $district === null
+ && $city === null
+ && $postalCode === null
+ && $countryCode === null
+ && $country === null
+ ) {
+ return null;
+ }
+
+ return new self(
+ formatted: $formatted,
+ street: $street,
+ houseNumber: $houseNumber,
+ district: $district,
+ city: $city,
+ postalCode: $postalCode,
+ countryCode: $countryCode,
+ country: $country,
+ );
+ }
+
+ /**
+ * @param array $data
+ */
+ private static function resolveFormattedAddress(
+ array $data,
+ ?string $street,
+ ?string $houseNumber,
+ ?string $district,
+ ?string $city,
+ ?string $postalCode,
+ ): ?string {
+ $textAddress = self::nullableString($data['textovaAdresa'] ?? null);
+
+ if ($textAddress !== null) {
+ return $textAddress;
+ }
+
+ $addressParts = array_values(array_filter([
+ self::combineStreetAndHouseNumber($street, $houseNumber),
+ $district,
+ $postalCode !== null && $city !== null ? "{$postalCode} {$city}" : $city,
+ $postalCode !== null && $city === null ? $postalCode : null,
+ ]));
+
+ if ($addressParts === []) {
+ return null;
+ }
+
+ return implode(', ', $addressParts);
+ }
+
+ private static function combineStreetAndHouseNumber(?string $street, ?string $houseNumber): ?string
+ {
+ if ($street === null && $houseNumber === null) {
+ return null;
+ }
+
+ if ($street !== null && $houseNumber !== null) {
+ return "{$street} {$houseNumber}";
+ }
+
+ return $street ?? $houseNumber;
+ }
+
+ /**
+ * @param array $data
+ */
+ private static function resolveHouseNumber(array $data): ?string
+ {
+ $descriptiveNumber = self::scalarToString($data['cisloDomovni'] ?? null);
+ $orientationNumber = self::scalarToString($data['cisloOrientacni'] ?? null);
+ $orientationLetter = self::nullableString($data['cisloOrientacniPismeno'] ?? null) ?? '';
+
+ if ($descriptiveNumber === null && $orientationNumber === null) {
+ return null;
+ }
+
+ if ($descriptiveNumber !== null && $orientationNumber !== null) {
+ return $descriptiveNumber.'/'.$orientationNumber.$orientationLetter;
+ }
+
+ return $descriptiveNumber ?? $orientationNumber.$orientationLetter;
+ }
+
+ private static function nullableString(mixed $value): ?string
+ {
+ $string = self::scalarToString($value);
+
+ return $string === null || $string === '' ? null : $string;
+ }
+
+ private static function scalarToString(mixed $value): ?string
+ {
+ if (! is_scalar($value)) {
+ return null;
+ }
+
+ return trim((string) $value);
+ }
+}
diff --git a/src/Data/CompanyData.php b/src/Data/CompanyData.php
new file mode 100644
index 0000000..5797e43
--- /dev/null
+++ b/src/Data/CompanyData.php
@@ -0,0 +1,83 @@
+ $rawData
+ */
+ public function __construct(
+ public readonly string $ic,
+ public readonly string $name,
+ public readonly ?string $dic,
+ public readonly ?string $dicSkDph,
+ public readonly ?AddressData $registeredOffice,
+ public readonly ?DeliveryAddressData $deliveryAddress,
+ public readonly RegistrationData $registration,
+ public readonly array $rawData,
+ ) {}
+
+ /**
+ * @param array $data
+ *
+ * @throws InvalidApiResponseException
+ */
+ public static function fromApiResponse(array $data): self
+ {
+ return new self(
+ ic: self::requiredString($data, 'ico'),
+ name: self::requiredString($data, 'obchodniJmeno'),
+ dic: self::nullableString($data['dic'] ?? null),
+ dicSkDph: self::nullableString($data['dicSkDph'] ?? null),
+ registeredOffice: AddressData::fromApiResponse(self::arrayData($data['sidlo'] ?? null)),
+ deliveryAddress: DeliveryAddressData::fromApiResponse(self::arrayData($data['adresaDorucovaci'] ?? null)),
+ registration: RegistrationData::fromApiResponse($data),
+ rawData: $data,
+ );
+ }
+
+ /**
+ * @param array $data
+ *
+ * @throws InvalidApiResponseException
+ */
+ private static function requiredString(array $data, string $field): string
+ {
+ $value = self::nullableString($data[$field] ?? null);
+
+ if ($value === null) {
+ throw InvalidApiResponseException::missingRequiredField($field);
+ }
+
+ return $value;
+ }
+
+ private static function nullableString(mixed $value): ?string
+ {
+ if (! is_scalar($value)) {
+ return null;
+ }
+
+ $string = trim((string) $value);
+
+ return $string === '' ? null : $string;
+ }
+
+ /**
+ * @return array
+ */
+ private static function arrayData(mixed $value): array
+ {
+ if (! is_array($value)) {
+ return [];
+ }
+
+ /** @var array $value */
+ return $value;
+ }
+}
diff --git a/src/Data/DeliveryAddressData.php b/src/Data/DeliveryAddressData.php
new file mode 100644
index 0000000..163ae6a
--- /dev/null
+++ b/src/Data/DeliveryAddressData.php
@@ -0,0 +1,48 @@
+ $lines
+ */
+ public function __construct(
+ public readonly array $lines,
+ public readonly ?string $formatted,
+ ) {}
+
+ /**
+ * @param array $data
+ */
+ public static function fromApiResponse(array $data): ?self
+ {
+ $lines = array_values(array_filter([
+ self::nullableString($data['radekAdresy1'] ?? null),
+ self::nullableString($data['radekAdresy2'] ?? null),
+ self::nullableString($data['radekAdresy3'] ?? null),
+ ]));
+
+ if ($lines === []) {
+ return null;
+ }
+
+ return new self(
+ lines: $lines,
+ formatted: implode(', ', $lines),
+ );
+ }
+
+ private static function nullableString(mixed $value): ?string
+ {
+ if (! is_scalar($value)) {
+ return null;
+ }
+
+ $string = trim((string) $value);
+
+ return $string === '' ? null : $string;
+ }
+}
diff --git a/src/Data/RegistrationData.php b/src/Data/RegistrationData.php
new file mode 100644
index 0000000..2d00004
--- /dev/null
+++ b/src/Data/RegistrationData.php
@@ -0,0 +1,180 @@
+ $naceCodes
+ * @param list $nace2008Codes
+ * @param list $sourceStatuses
+ */
+ public function __construct(
+ public readonly ?string $legalForm,
+ public readonly ?string $financialOffice,
+ public readonly ?string $dateOfEstablishment,
+ public readonly ?string $dateOfLastUpdate,
+ public readonly ?string $primarySource,
+ public readonly ?string $businessRegisterFileMark,
+ public readonly array $naceCodes,
+ public readonly array $nace2008Codes,
+ public readonly array $sourceStatuses,
+ ) {}
+
+ /**
+ * @param array $data
+ */
+ public static function fromApiResponse(array $data): self
+ {
+ return new self(
+ legalForm: self::nullableString($data['pravniForma'] ?? null),
+ financialOffice: self::nullableString($data['financniUrad'] ?? null),
+ dateOfEstablishment: self::nullableString($data['datumVzniku'] ?? null),
+ dateOfLastUpdate: self::nullableString($data['datumAktualizace'] ?? null),
+ primarySource: self::nullableString($data['primarniZdroj'] ?? null),
+ businessRegisterFileMark: self::resolveBusinessRegisterFileMark(self::listOfAssociativeArrays($data['dalsiUdaje'] ?? null)),
+ naceCodes: self::normalizeStringList(self::listData($data['czNace'] ?? null)),
+ nace2008Codes: self::normalizeStringList(self::listData($data['czNace2008'] ?? null)),
+ sourceStatuses: self::normalizeSourceStatuses(self::associativeArrayData($data['seznamRegistraci'] ?? null)),
+ );
+ }
+
+ public function sourceStatus(string $source): ?RegistrationStatusData
+ {
+ foreach ($this->sourceStatuses as $sourceStatus) {
+ if ($sourceStatus->source === $source) {
+ return $sourceStatus;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param array> $additionalData
+ */
+ private static function resolveBusinessRegisterFileMark(array $additionalData): ?string
+ {
+ foreach ($additionalData as $entry) {
+ $fileMark = self::nullableString($entry['spisovaZnacka'] ?? null);
+
+ if ($fileMark !== null) {
+ return $fileMark;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param array $values
+ * @return list
+ */
+ private static function normalizeStringList(array $values): array
+ {
+ $normalized = [];
+
+ foreach ($values as $value) {
+ $string = self::nullableString($value);
+
+ if ($string !== null) {
+ $normalized[] = $string;
+ }
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * @param array $registrationStatuses
+ * @return list
+ */
+ private static function normalizeSourceStatuses(array $registrationStatuses): array
+ {
+ $normalized = [];
+
+ foreach ($registrationStatuses as $key => $value) {
+ $rawStatus = self::nullableString($value);
+
+ if ($rawStatus === null) {
+ continue;
+ }
+
+ $normalized[] = new RegistrationStatusData(
+ source: self::resolveSourceName($key),
+ rawStatus: $rawStatus,
+ status: RegistrationSourceState::tryFromApi($rawStatus),
+ );
+ }
+
+ return $normalized;
+ }
+
+ private static function resolveSourceName(string $key): string
+ {
+ $prefix = 'stavZdroje';
+
+ if (str_starts_with($key, $prefix)) {
+ return lcfirst(substr($key, strlen($prefix)));
+ }
+
+ return $key;
+ }
+
+ /**
+ * @return list
+ */
+ private static function listData(mixed $value): array
+ {
+ return is_array($value) ? array_values($value) : [];
+ }
+
+ /**
+ * @return array
+ */
+ private static function associativeArrayData(mixed $value): array
+ {
+ if (! is_array($value)) {
+ return [];
+ }
+
+ /** @var array $value */
+ return $value;
+ }
+
+ /**
+ * @return array>
+ */
+ private static function listOfAssociativeArrays(mixed $value): array
+ {
+ if (! is_array($value)) {
+ return [];
+ }
+
+ $normalized = [];
+
+ foreach ($value as $entry) {
+ if (is_array($entry)) {
+ /** @var array $entry */
+ $normalized[] = $entry;
+ }
+ }
+
+ return $normalized;
+ }
+
+ private static function nullableString(mixed $value): ?string
+ {
+ if (! is_scalar($value)) {
+ return null;
+ }
+
+ $string = trim((string) $value);
+
+ return $string === '' ? null : $string;
+ }
+}
diff --git a/src/Data/RegistrationStatusData.php b/src/Data/RegistrationStatusData.php
new file mode 100644
index 0000000..8c48bcf
--- /dev/null
+++ b/src/Data/RegistrationStatusData.php
@@ -0,0 +1,16 @@
+|null findCompanyRaw(string $ic)
+ * @method static \NyonCode\Ares\Data\CompanyData findCompanyOrFail(string $ic)
+ * @method static bool forgetCompany(string $ic)
+ * @method static bool isValidIc(string $ic)
+ * @method static string normalizeIc(string $ic)
+ *
+ * @see AresClientInterface
+ */
+final class Ares extends Facade
+{
+ protected static function getFacadeAccessor(): string
+ {
+ return AresClientInterface::class;
+ }
+}
diff --git a/src/Providers/AresServiceProvider.php b/src/Providers/AresServiceProvider.php
new file mode 100644
index 0000000..e88f41f
--- /dev/null
+++ b/src/Providers/AresServiceProvider.php
@@ -0,0 +1,83 @@
+name('laravel-ares')
+ ->hasConfig()
+ ->hasCommands([
+ TestAresCommand::class,
+ ])
+ ->hasTranslations('resources/lang');
+ }
+
+ public function register(): void
+ {
+ parent::register();
+
+ $this->app->singleton(AresClientInterface::class, function (Application $app): AresClient {
+ return new AresClient(
+ baseUrl: $this->configString('ares.api_url'),
+ cacheTtl: $this->configInt('ares.cache_ttl'),
+ logger: $app->make(LogManager::class)->channel($this->configString('ares.log_channel')),
+ events: $app->make(Dispatcher::class),
+ cache: $app->make(CacheFactory::class)->store(),
+ http: $app->make(Http::class),
+ );
+ });
+
+ $this->app->bind('ares', fn (Application $app) => $app->make(AresClientInterface::class));
+ }
+
+ /**
+ * @return array
+ */
+ public function aboutData(): array
+ {
+ return [
+ 'Client contract' => AresClientInterface::class,
+ 'Facade alias' => 'Ares',
+ 'Cache support' => 'enabled',
+ ];
+ }
+
+ private function configString(string $key): string
+ {
+ $value = config($key);
+
+ return is_string($value) ? $value : '';
+ }
+
+ private function configInt(string $key): int
+ {
+ $value = config($key);
+
+ return is_int($value) ? $value : (is_numeric($value) ? (int) $value : 0);
+ }
+}
diff --git a/src/Services/AresClient.php b/src/Services/AresClient.php
new file mode 100644
index 0000000..3e0e15c
--- /dev/null
+++ b/src/Services/AresClient.php
@@ -0,0 +1,183 @@
+baseUrl = rtrim($baseUrl, '/');
+ $this->cacheTtl = $cacheTtl;
+ $this->logger = $logger;
+ $this->events = $events;
+ $this->cache = $cache;
+ }
+
+ public function findCompany(string $ic): ?CompanyData
+ {
+ $ic = $this->normalizeIc($ic);
+
+ if (! $this->isValidIc($ic)) {
+ $this->logger->warning('Invalid IC format', ['ic' => $ic]);
+
+ return null;
+ }
+
+ $cacheKey = $this->cacheKey($ic);
+
+ return $this->cache->remember($cacheKey, $this->cacheTtl, function () use ($ic) {
+ try {
+ $response = $this->http
+ ->timeout($this->httpTimeout())
+ ->connectTimeout($this->httpConnectTimeout())
+ ->get("{$this->baseUrl}/ekonomicke-subjekty/{$ic}");
+
+ if ($response->failed()) {
+ $this->events->dispatch(new CompanyLookupFailed($ic, $response->status()));
+
+ return null;
+ }
+
+ $company = CompanyData::fromApiResponse(
+ $this->payloadData($response->json())
+ );
+ $this->events->dispatch(new CompanyLookupSucceeded($company));
+
+ return $company;
+ } catch (Throwable $e) {
+ $this->logger->error('ARES API error', [
+ 'ic' => $ic,
+ 'exception' => $e->getMessage(),
+ ]);
+ $this->events->dispatch(new CompanyLookupFailed($ic, 0, $e));
+
+ return null;
+ }
+ });
+ }
+
+ public function findCompanyRaw(string $ic): ?array
+ {
+ return $this->findCompany($ic)?->rawData;
+ }
+
+ public function findCompanyOrFail(string $ic): CompanyData
+ {
+ $normalizedIc = $this->normalizeIc($ic);
+
+ if (! $this->isValidIc($normalizedIc)) {
+ throw InvalidIcException::forIc($normalizedIc);
+ }
+
+ $company = $this->findCompany($normalizedIc);
+
+ if ($company === null) {
+ throw CompanyNotFoundException::forIc($normalizedIc);
+ }
+
+ return $company;
+ }
+
+ public function forgetCompany(string $ic): bool
+ {
+ return $this->cache->forget($this->cacheKey($this->normalizeIc($ic)));
+ }
+
+ public function isValidIc(string $ic): bool
+ {
+ $ic = $this->normalizeIc($ic);
+
+ if (! preg_match('/^\d{8}$/', $ic)) {
+ return false;
+ }
+
+ $sum = 0;
+ for ($i = 0; $i < 7; $i++) {
+ $sum += (int) $ic[$i] * (8 - $i);
+ }
+
+ $remainder = $sum % 11;
+ $checksum = match ($remainder) {
+ 0 => 1,
+ 1 => 0,
+ default => 11 - $remainder,
+ };
+
+ return $checksum === (int) $ic[7];
+ }
+
+ public function normalizeIc(string $ic): string
+ {
+ return str_pad(preg_replace('/\D/', '', $ic) ?? '', 8, '0', STR_PAD_LEFT);
+ }
+
+ private function cacheKey(string $ic): string
+ {
+ return "ares:company:{$ic}";
+ }
+
+ private function httpTimeout(): float
+ {
+ $timeout = config('ares.http_options.timeout');
+
+ return is_numeric($timeout) ? (float) $timeout : 5.0;
+ }
+
+ private function httpConnectTimeout(): float
+ {
+ $timeout = config('ares.http_options.connect_timeout');
+
+ return is_numeric($timeout) ? (float) $timeout : 3.0;
+ }
+
+ /**
+ * @return array
+ */
+ private function payloadData(mixed $payload): array
+ {
+ if (! is_array($payload)) {
+ throw InvalidApiResponseException::invalidPayloadType();
+ }
+
+ $normalized = [];
+
+ foreach ($payload as $key => $value) {
+ if (is_string($key)) {
+ $normalized[$key] = $value;
+ }
+ }
+
+ return $normalized;
+ }
+}
diff --git a/tests/Feature/AresClientTest.php b/tests/Feature/AresClientTest.php
new file mode 100644
index 0000000..e74a6d0
--- /dev/null
+++ b/tests/Feature/AresClientTest.php
@@ -0,0 +1,243 @@
+ Http::response([
+ 'ico' => '27074358',
+ 'obchodniJmeno' => 'Asseco Central Europe, a.s.',
+ 'dic' => 'CZ27074358',
+ 'pravniForma' => '121',
+ 'financniUrad' => '004',
+ 'datumVzniku' => '2003-08-06',
+ 'datumAktualizace' => '2026-03-08',
+ 'primarniZdroj' => 'ros',
+ 'sidlo' => [
+ 'kodStatu' => 'CZ',
+ 'nazevStatu' => 'Ceska republika',
+ 'nazevUlice' => 'Budejovicka',
+ 'cisloDomovni' => 778,
+ 'cisloOrientacni' => 3,
+ 'cisloOrientacniPismeno' => 'a',
+ 'nazevCastiObce' => 'Michle',
+ 'obec' => 'Praha',
+ 'nazevObce' => 'Praha',
+ 'psc' => 14000,
+ 'textovaAdresa' => 'Budejovicka 778/3a, Michle, 14000 Praha 4',
+ ],
+ 'adresaDorucovaci' => [
+ 'radekAdresy1' => 'Budejovicka 778/3a',
+ 'radekAdresy2' => 'Michle',
+ 'radekAdresy3' => '14000 Praha 4',
+ ],
+ 'czNace' => ['62', '63'],
+ 'czNace2008' => ['620', '63'],
+ 'seznamRegistraci' => [
+ 'stavZdrojeRos' => 'AKTIVNI',
+ 'stavZdrojeVr' => 'AKTIVNI',
+ ],
+ 'dalsiUdaje' => [
+ [
+ 'datovyZdroj' => 'vr',
+ 'spisovaZnacka' => 'B 8525/MSPH',
+ ],
+ ],
+ ]),
+ ]);
+
+ $company = app(AresClientInterface::class)->findCompany('27074358');
+
+ expect($company)
+ ->not->toBeNull()
+ ->and($company->ic)->toBe('27074358')
+ ->and($company->name)->toBe('Asseco Central Europe, a.s.')
+ ->and($company->dic)->toBe('CZ27074358')
+ ->and($company->registeredOffice?->formatted)->toBe('Budejovicka 778/3a, Michle, 14000 Praha 4')
+ ->and($company->registeredOffice?->houseNumber)->toBe('778/3a')
+ ->and($company->deliveryAddress?->lines)->toBe([
+ 'Budejovicka 778/3a',
+ 'Michle',
+ '14000 Praha 4',
+ ])
+ ->and($company->registration->financialOffice)->toBe('004')
+ ->and($company->registration->dateOfEstablishment)->toBe('2003-08-06')
+ ->and($company->registration->primarySource)->toBe('ros')
+ ->and($company->registration->businessRegisterFileMark)->toBe('B 8525/MSPH')
+ ->and($company->registration->naceCodes)->toBe(['62', '63'])
+ ->and($company->registration->sourceStatus('ros')?->status)->toBe(RegistrationSourceState::Active);
+
+ Event::assertDispatched(CompanyLookupSucceeded::class, fn (CompanyLookupSucceeded $event) => $event->company->ic === '27074358');
+ Event::assertNotDispatched(CompanyLookupFailed::class);
+});
+
+it('caches successful company lookups', function () {
+ $requestCount = 0;
+
+ Http::fake(function () use (&$requestCount) {
+ $requestCount++;
+
+ return Http::response([
+ 'ico' => '27074358',
+ 'obchodniJmeno' => 'Asseco Central Europe, a.s.',
+ 'sidlo' => [
+ 'nazevObce' => 'Praha',
+ ],
+ ]);
+ });
+
+ $client = app(AresClientInterface::class);
+
+ $first = $client->findCompany('27074358');
+ $second = $client->findCompany('27074358');
+
+ expect($first?->ic)->toBe('27074358')
+ ->and($second?->ic)->toBe('27074358')
+ ->and($requestCount)->toBe(1);
+});
+
+it('dispatches a failed event when the API returns an error response', function () {
+ Event::fake();
+
+ Http::fake([
+ 'https://ares.gov.cz/*' => Http::response([], 404),
+ ]);
+
+ $company = app(AresClientInterface::class)->findCompany('27074358');
+
+ expect($company)->toBeNull();
+
+ Event::assertDispatched(
+ CompanyLookupFailed::class,
+ fn (CompanyLookupFailed $event) => $event->ic === '27074358'
+ && $event->httpStatus === 404
+ && $event->exception === null
+ );
+});
+
+it('returns raw api data through the convenience method', function () {
+ Http::fake([
+ 'https://ares.gov.cz/*' => Http::response([
+ 'ico' => '27074358',
+ 'obchodniJmeno' => 'Asseco Central Europe, a.s.',
+ 'dic' => 'CZ27074358',
+ ]),
+ ]);
+
+ $raw = app(AresClientInterface::class)->findCompanyRaw('27074358');
+
+ expect($raw)->toMatchArray([
+ 'ico' => '27074358',
+ 'obchodniJmeno' => 'Asseco Central Europe, a.s.',
+ 'dic' => 'CZ27074358',
+ ]);
+});
+
+it('can forget a cached company lookup across normalized ic values', function () {
+ $requestCount = 0;
+
+ Http::fake(function () use (&$requestCount) {
+ $requestCount++;
+
+ return Http::response([
+ 'ico' => '27074358',
+ 'obchodniJmeno' => 'Asseco Central Europe, a.s.',
+ ]);
+ });
+
+ $client = app(AresClientInterface::class);
+
+ $client->findCompany('27 074 358');
+ $client->forgetCompany('27074358');
+ $client->findCompany('27074358');
+
+ expect($requestCount)->toBe(2);
+});
+
+it('throws a domain exception for invalid ic in the fail-fast method', function () {
+ expect(fn () => app(AresClientInterface::class)->findCompanyOrFail('123'))
+ ->toThrow(InvalidIcException::class, 'Invalid ICO [00000123].');
+});
+
+it('throws a domain exception when a company is not found in the fail-fast method', function () {
+ Http::fake([
+ 'https://ares.gov.cz/*' => Http::response([], 404),
+ ]);
+
+ expect(fn () => app(AresClientInterface::class)->findCompanyOrFail('27074358'))
+ ->toThrow(CompanyNotFoundException::class, 'Company with ICO [27074358] was not found in ARES.');
+});
+
+it('handles transport exceptions and dispatches a failed event', function () {
+ Event::fake();
+
+ Http::fake(fn () => throw new ConnectionException('Connection refused.'));
+
+ $company = app(AresClientInterface::class)->findCompany('27074358');
+
+ expect($company)->toBeNull();
+
+ Event::assertDispatched(
+ CompanyLookupFailed::class,
+ fn (CompanyLookupFailed $event) => $event->ic === '27074358'
+ && $event->httpStatus === 0
+ && $event->exception instanceof ConnectionException
+ );
+});
+
+it('treats malformed payloads as failed lookups', function () {
+ Event::fake();
+
+ Http::fake([
+ 'https://ares.gov.cz/*' => Http::response([
+ 'ico' => '27074358',
+ ]),
+ ]);
+
+ $company = app(AresClientInterface::class)->findCompany('27074358');
+
+ expect($company)->toBeNull();
+
+ Event::assertDispatched(
+ CompanyLookupFailed::class,
+ fn (CompanyLookupFailed $event) => $event->ic === '27074358'
+ && $event->exception instanceof InvalidApiResponseException
+ );
+});
+
+it('rejects invalid IC values before making an HTTP request', function () {
+ Http::fake();
+
+ $company = app(AresClientInterface::class)->findCompany('123');
+
+ expect($company)->toBeNull();
+ Http::assertNothingSent();
+});
+
+it('validates normalized IC values correctly', function () {
+ $client = app(AresClientInterface::class);
+
+ expect($client->isValidIc('27074358'))->toBeTrue()
+ ->and($client->isValidIc(' 27 074 358 '))->toBeTrue()
+ ->and($client->isValidIc('12345678'))->toBeFalse();
+});
+
+it('normalizes ic values for consistent lookups', function () {
+ $client = app(AresClientInterface::class);
+
+ expect($client->normalizeIc('27 074 358'))->toBe('27074358')
+ ->and($client->normalizeIc('123'))->toBe('00000123');
+});
diff --git a/tests/Feature/TestAresCommandTest.php b/tests/Feature/TestAresCommandTest.php
new file mode 100644
index 0000000..926c24a
--- /dev/null
+++ b/tests/Feature/TestAresCommandTest.php
@@ -0,0 +1,139 @@
+app->instance(AresClientInterface::class, new class implements AresClientInterface
+ {
+ public function findCompany(string $ic): ?CompanyData
+ {
+ return new CompanyData(
+ ic: '27074358',
+ name: 'Asseco Central Europe, a.s.',
+ dic: 'CZ27074358',
+ dicSkDph: null,
+ registeredOffice: new AddressData(
+ formatted: 'Budejovicka 778/3a, Michle, 14000 Praha 4',
+ street: 'Budejovicka',
+ houseNumber: '778/3a',
+ district: 'Michle',
+ city: 'Praha',
+ postalCode: '14000',
+ countryCode: 'CZ',
+ country: 'Ceska republika',
+ ),
+ deliveryAddress: new DeliveryAddressData(
+ lines: [
+ 'Budejovicka 778/3a',
+ 'Michle',
+ '14000 Praha 4',
+ ],
+ formatted: 'Budejovicka 778/3a, Michle, 14000 Praha 4',
+ ),
+ registration: new RegistrationData(
+ legalForm: '121',
+ financialOffice: '004',
+ dateOfEstablishment: '2003-08-06',
+ dateOfLastUpdate: '2026-03-08',
+ primarySource: 'ros',
+ businessRegisterFileMark: 'B 8525/MSPH',
+ naceCodes: ['62', '63'],
+ nace2008Codes: ['620', '63'],
+ sourceStatuses: [],
+ ),
+ rawData: []
+ );
+ }
+
+ public function findCompanyRaw(string $ic): ?array
+ {
+ return $this->findCompany($ic)?->rawData;
+ }
+
+ public function findCompanyOrFail(string $ic): CompanyData
+ {
+ return $this->findCompany($ic) ?? throw CompanyNotFoundException::forIc($ic);
+ }
+
+ public function forgetCompany(string $ic): bool
+ {
+ return true;
+ }
+
+ public function isValidIc(string $ic): bool
+ {
+ return true;
+ }
+
+ public function normalizeIc(string $ic): string
+ {
+ return $ic;
+ }
+ });
+
+ $this->artisan('ares:test', ['ic' => '27074358'])
+ ->expectsOutputToContain('Company found:')
+ ->expectsTable(
+ ['Property', 'Value'],
+ [
+ ['IC', '27074358'],
+ ['Name', 'Asseco Central Europe, a.s.'],
+ ['DIC', 'CZ27074358'],
+ ['Primary Source', 'ros'],
+ ['Date of Establishment', '2003-08-06'],
+ ['Financial Office', '004'],
+ ['Address', 'Budejovicka 778/3a, Michle, 14000 Praha 4'],
+ ['Delivery Address', 'Budejovicka 778/3a, Michle, 14000 Praha 4'],
+ ['Legal Form', '121'],
+ ['Business Register File Mark', 'B 8525/MSPH'],
+ ]
+ )
+ ->assertExitCode(0);
+});
+
+it('renders a translated error when the company is not found', function () {
+ $this->app->instance(AresClientInterface::class, new class implements AresClientInterface
+ {
+ public function findCompany(string $ic): ?CompanyData
+ {
+ return null;
+ }
+
+ public function findCompanyRaw(string $ic): ?array
+ {
+ return null;
+ }
+
+ public function findCompanyOrFail(string $ic): CompanyData
+ {
+ throw InvalidIcException::forIc($ic);
+ }
+
+ public function forgetCompany(string $ic): bool
+ {
+ return true;
+ }
+
+ public function isValidIc(string $ic): bool
+ {
+ return false;
+ }
+
+ public function normalizeIc(string $ic): string
+ {
+ return $ic;
+ }
+ });
+
+ $this->artisan('ares:test', ['ic' => '00000000'])
+ ->expectsOutputToContain("Subjekt s I\u{010C}O 00000000 nebyl nalezen.")
+ ->assertExitCode(1);
+});
diff --git a/tests/Pest.php b/tests/Pest.php
new file mode 100644
index 0000000..a38d9d3
--- /dev/null
+++ b/tests/Pest.php
@@ -0,0 +1,7 @@
+in('Feature', 'Unit');
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..9d850a9
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,29 @@
+set('app.key', 'base64:'.base64_encode(str_repeat('a', 32)));
+ $app['config']->set('app.locale', 'cs');
+ $app['config']->set('app.fallback_locale', 'en');
+ $app['config']->set('cache.default', 'array');
+ $app['config']->set('ares.api_url', 'https://ares.gov.cz/ekonomicke-subjekty-v-be/rest');
+ $app['config']->set('ares.cache_ttl', 3600);
+ $app['config']->set('ares.log_channel', 'stack');
+ $app['config']->set('ares.http_options.timeout', 5.0);
+ $app['config']->set('ares.http_options.connect_timeout', 3.0);
+ }
+}
diff --git a/tests/Unit/CompanyDataTest.php b/tests/Unit/CompanyDataTest.php
new file mode 100644
index 0000000..546af65
--- /dev/null
+++ b/tests/Unit/CompanyDataTest.php
@@ -0,0 +1,115 @@
+ '27074358',
+ 'obchodniJmeno' => 'Asseco Central Europe, a.s.',
+ 'dic' => 'CZ27074358',
+ 'pravniForma' => '121',
+ 'financniUrad' => '004',
+ 'datumVzniku' => '2003-08-06',
+ 'datumAktualizace' => '2026-03-08',
+ 'primarniZdroj' => 'ros',
+ 'sidlo' => [
+ 'kodStatu' => 'CZ',
+ 'nazevStatu' => 'Ceska republika',
+ 'nazevUlice' => 'Budejovicka',
+ 'cisloDomovni' => 778,
+ 'cisloOrientacni' => 3,
+ 'cisloOrientacniPismeno' => 'a',
+ 'nazevCastiObce' => 'Michle',
+ 'obec' => 'Praha',
+ 'nazevObce' => 'Praha',
+ 'psc' => 14000,
+ 'textovaAdresa' => 'Budejovicka 778/3a, Michle, 14000 Praha 4',
+ ],
+ 'adresaDorucovaci' => [
+ 'radekAdresy1' => 'Budejovicka 778/3a',
+ 'radekAdresy2' => 'Michle',
+ 'radekAdresy3' => '14000 Praha 4',
+ ],
+ 'czNace' => ['62', '63'],
+ 'czNace2008' => ['620', '63'],
+ 'seznamRegistraci' => [
+ 'stavZdrojeRos' => 'AKTIVNI',
+ 'stavZdrojeVr' => 'AKTIVNI',
+ ],
+ 'dalsiUdaje' => [
+ [
+ 'datovyZdroj' => 'vr',
+ 'spisovaZnacka' => 'B 8525/MSPH',
+ ],
+ ],
+ ]);
+
+ expect($company->ic)->toBe('27074358')
+ ->and($company->name)->toBe('Asseco Central Europe, a.s.')
+ ->and($company->dic)->toBe('CZ27074358')
+ ->and($company->registeredOffice?->formatted)->toBe('Budejovicka 778/3a, Michle, 14000 Praha 4')
+ ->and($company->registeredOffice?->street)->toBe('Budejovicka')
+ ->and($company->registeredOffice?->houseNumber)->toBe('778/3a')
+ ->and($company->registeredOffice?->district)->toBe('Michle')
+ ->and($company->registeredOffice?->city)->toBe('Praha')
+ ->and($company->registeredOffice?->postalCode)->toBe('14000')
+ ->and($company->deliveryAddress?->lines)->toBe([
+ 'Budejovicka 778/3a',
+ 'Michle',
+ '14000 Praha 4',
+ ])
+ ->and($company->registration->legalForm)->toBe('121')
+ ->and($company->registration->financialOffice)->toBe('004')
+ ->and($company->registration->dateOfEstablishment)->toBe('2003-08-06')
+ ->and($company->registration->dateOfLastUpdate)->toBe('2026-03-08')
+ ->and($company->registration->primarySource)->toBe('ros')
+ ->and($company->registration->businessRegisterFileMark)->toBe('B 8525/MSPH')
+ ->and($company->registration->naceCodes)->toBe(['62', '63'])
+ ->and($company->registration->nace2008Codes)->toBe(['620', '63'])
+ ->and($company->registration->sourceStatus('ros')?->status)->toBe(RegistrationSourceState::Active)
+ ->and($company->registration->sourceStatus('vr')?->rawStatus)->toBe('AKTIVNI');
+});
+
+it('keeps nullable nested data safe when the api response is partial', function () {
+ $company = CompanyData::fromApiResponse([
+ 'ico' => '27074358',
+ 'obchodniJmeno' => 'Example Company s.r.o.',
+ ]);
+
+ expect($company->registeredOffice)->toBeNull()
+ ->and($company->deliveryAddress)->toBeNull()
+ ->and($company->dic)->toBeNull()
+ ->and($company->registration->naceCodes)->toBe([])
+ ->and($company->registration->nace2008Codes)->toBe([])
+ ->and($company->registration->sourceStatuses)->toBe([])
+ ->and($company->registration->businessRegisterFileMark)->toBeNull()
+ ->and($company->registration->legalForm)->toBeNull();
+});
+
+it('builds a formatted address when text address is missing', function () {
+ $company = CompanyData::fromApiResponse([
+ 'ico' => '27074358',
+ 'obchodniJmeno' => 'Example Company s.r.o.',
+ 'sidlo' => [
+ 'nazevUlice' => 'Budejovicka',
+ 'cisloDomovni' => 778,
+ 'cisloOrientacni' => 3,
+ 'cisloOrientacniPismeno' => 'a',
+ 'nazevCastiObce' => 'Michle',
+ 'nazevObce' => 'Praha',
+ 'psc' => 14000,
+ ],
+ ]);
+
+ expect($company->registeredOffice?->formatted)->toBe('Budejovicka 778/3a, Michle, 14000 Praha');
+});
+
+it('fails fast on malformed api responses missing required fields', function () {
+ expect(fn () => CompanyData::fromApiResponse([
+ 'ico' => '27074358',
+ ]))->toThrow(InvalidApiResponseException::class, 'ARES response is missing required field [obchodniJmeno].');
+});
diff --git a/tests/Unit/ServiceProviderTest.php b/tests/Unit/ServiceProviderTest.php
new file mode 100644
index 0000000..28e972b
--- /dev/null
+++ b/tests/Unit/ServiceProviderTest.php
@@ -0,0 +1,11 @@
+toBeInstanceOf(AresClient::class)
+ ->and(app('ares'))->toBeInstanceOf(AresClientInterface::class);
+});