output:
Chunk ID: 712d23
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 7693
Output:
routes/web.php:46:Route::middleware(['storefront'])->group(function (): void {
routes/web.php:103:Route::middleware(['storefront'])->group(function (): void {
routes/api.php:15:    ->prefix('storefront/v1')
routes/api.php:16:    ->name('api.storefront.v1.')
routes/api.php:18:        Route::middleware('throttle:api.storefront')->group(function (): void {
app/Models/Concerns/BelongsToStore.php:15:            if ($model->store_id || ! app()->bound('current_store')) {
app/Models/Concerns/BelongsToStore.php:19:            $store = app('current_store');
tests/Feature/Orders/OrderServiceTest.php:34:    app()->instance('current_store', $store);
tests/Feature/Orders/BankTransferOrderTest.php:32:    app()->instance('current_store', $store);
tests/Feature/Orders/RefundServiceTest.php:28:    app()->instance('current_store', $store);
tests/Feature/Orders/FulfillmentServiceTest.php:28:    app()->instance('current_store', $store);
tests/Feature/Api/StorefrontCheckoutApiTest.php:20:function storefrontApiCheckoutStore(): Store
tests/Feature/Api/StorefrontCheckoutApiTest.php:25:function storefrontApiCheckoutVariant(Store $store): ProductVariant
tests/Feature/Api/StorefrontCheckoutApiTest.php:50:function storefrontApiCheckoutAddress(string $country = 'DE'): array
tests/Feature/Api/StorefrontCheckoutApiTest.php:64:test('storefront checkout api progresses through address shipping discount removal and payment selection', function (): void {
tests/Feature/Api/StorefrontCheckoutApiTest.php:65:    $store = storefrontApiCheckoutStore();
tests/Feature/Api/StorefrontCheckoutApiTest.php:66:    $variant = storefrontApiCheckoutVariant($store);
tests/Feature/Api/StorefrontCheckoutApiTest.php:70:        ->postJson('/api/storefront/v1/carts')
tests/Feature/Api/StorefrontCheckoutApiTest.php:74:        ->postJson("/api/storefront/v1/carts/{$cartId}/lines", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:81:        ->postJson('/api/storefront/v1/checkouts', [
tests/Feature/Api/StorefrontCheckoutApiTest.php:96:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/address", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:97:            'shipping_address' => storefrontApiCheckoutAddress(),
tests/Feature/Api/StorefrontCheckoutApiTest.php:109:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/shipping-method", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:118:        ->postJson("/api/storefront/v1/checkouts/{$checkoutId}/apply-discount", ['code' => 'SAVE10'])
tests/Feature/Api/StorefrontCheckoutApiTest.php:124:        ->deleteJson("/api/storefront/v1/checkouts/{$checkoutId}/discount")
tests/Feature/Api/StorefrontCheckoutApiTest.php:130:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/payment-method", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:144:test('storefront checkout api rejects invalid addresses shipping methods and discounts', function (): void {
tests/Feature/Api/StorefrontCheckoutApiTest.php:145:    $store = storefrontApiCheckoutStore();
tests/Feature/Api/StorefrontCheckoutApiTest.php:146:    $variant = storefrontApiCheckoutVariant($store);
tests/Feature/Api/StorefrontCheckoutApiTest.php:150:        ->postJson('/api/storefront/v1/carts')
tests/Feature/Api/StorefrontCheckoutApiTest.php:154:        ->postJson("/api/storefront/v1/carts/{$cartId}/lines", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:161:        ->postJson('/api/storefront/v1/checkouts', [
tests/Feature/Api/StorefrontCheckoutApiTest.php:168:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/address", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:169:            'shipping_address' => array_diff_key(storefrontApiCheckoutAddress(), ['first_name' => true]),
tests/Feature/Api/StorefrontCheckoutApiTest.php:175:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/address", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:176:            'shipping_address' => storefrontApiCheckoutAddress(),
tests/Feature/Api/StorefrontCheckoutApiTest.php:185:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/shipping-method", [
tests/Feature/Api/StorefrontCheckoutApiTest.php:191:        ->postJson("/api/storefront/v1/checkouts/{$checkoutId}/apply-discount", ['code' => 'NOTREAL'])
tests/Feature/Api/StorefrontSearchApiTest.php:17:function storefrontSearchApiStore(): Store
tests/Feature/Api/StorefrontSearchApiTest.php:22:test('storefront search api returns store scoped product results with facets', function (): void {
tests/Feature/Api/StorefrontSearchApiTest.php:23:    $store = storefrontSearchApiStore();
tests/Feature/Api/StorefrontSearchApiTest.php:56:        ->getJson("/api/storefront/v1/search?q=cloud%20cotton&filters={$filters}&sort=price_asc")
tests/Feature/Api/StorefrontSearchApiTest.php:68:test('storefront search api returns empty results for no matches and validates input', function (): void {
tests/Feature/Api/StorefrontSearchApiTest.php:70:        ->getJson('/api/storefront/v1/search?q=xyznonexistent')
tests/Feature/Api/StorefrontSearchApiTest.php:76:        ->getJson('/api/storefront/v1/search')
tests/Feature/Api/StorefrontSearchApiTest.php:81:        ->getJson('/api/storefront/v1/search?q=test&filters=not-json')
tests/Feature/Api/StorefrontSearchApiTest.php:86:test('storefront search api paginates results', function (): void {
tests/Feature/Api/StorefrontSearchApiTest.php:87:    $store = storefrontSearchApiStore();
tests/Feature/Api/StorefrontSearchApiTest.php:100:        ->getJson('/api/storefront/v1/search?q=api%20pagination%20linen&per_page=12&page=2')
tests/Feature/Api/StorefrontSearchApiTest.php:108:test('storefront search suggest api returns product and collection suggestions', function (): void {
tests/Feature/Api/StorefrontSearchApiTest.php:109:    $store = storefrontSearchApiStore();
tests/Feature/Api/StorefrontSearchApiTest.php:126:        ->getJson('/api/storefront/v1/search/suggest?q=alpaca&limit=5')
tests/Feature/Api/StorefrontAnalyticsApiTest.php:15:test('storefront analytics api accepts batches and deduplicates client events', function (): void {
tests/Feature/Api/StorefrontAnalyticsApiTest.php:24:                'properties' => ['url' => '/', 'channel' => 'storefront'],
tests/Feature/Api/StorefrontAnalyticsApiTest.php:38:        ->postJson('/api/storefront/v1/analytics/events', $payload)
tests/Feature/Api/StorefrontAnalyticsApiTest.php:44:        ->postJson('/api/storefront/v1/analytics/events', [
tests/Feature/Api/StorefrontAnalyticsApiTest.php:54:test('storefront analytics api validates event payload boundaries', function (): void {
tests/Feature/Api/StorefrontAnalyticsApiTest.php:56:        ->postJson('/api/storefront/v1/analytics/events', [
tests/Feature/Api/StorefrontAnalyticsApiTest.php:63:        ->postJson('/api/storefront/v1/analytics/events', [
tests/Feature/Catalog/ProductServiceTest.php:34:    app()->instance('current_store', $store);
tests/Feature/Catalog/ProductServiceTest.php:50:    app()->instance('current_store', $store);
tests/Feature/Catalog/ProductServiceTest.php:70:    app()->instance('current_store', $store);
tests/Feature/Catalog/ProductServiceTest.php:119:    app()->instance('current_store', $store);
tests/Feature/Catalog/ProductServiceTest.php:144:    app()->instance('current_store', $store);
tests/Feature/Api/StorefrontOrderApiTest.php:19:function storefrontOrderApiStore(): Store
tests/Feature/Api/StorefrontOrderApiTest.php:24:function storefrontOrderApiVariant(Store $store): ProductVariant
tests/Feature/Api/StorefrontOrderApiTest.php:49:function storefrontOrderApiCheckout(object $testCase, string $remoteAddress = '10.0.0.41'): array
tests/Feature/Api/StorefrontOrderApiTest.php:51:    $store = storefrontOrderApiStore();
tests/Feature/Api/StorefrontOrderApiTest.php:52:    $variant = storefrontOrderApiVariant($store);
tests/Feature/Api/StorefrontOrderApiTest.php:56:        ->postJson('/api/storefront/v1/carts')
tests/Feature/Api/StorefrontOrderApiTest.php:60:        ->postJson("/api/storefront/v1/carts/{$cartId}/lines", [
tests/Feature/Api/StorefrontOrderApiTest.php:67:        ->postJson('/api/storefront/v1/checkouts', [
tests/Feature/Api/StorefrontOrderApiTest.php:74:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/address", [
tests/Feature/Api/StorefrontOrderApiTest.php:87:        ->putJson("/api/storefront/v1/checkouts/{$checkoutId}/shipping-method", [
tests/Feature/Api/StorefrontOrderApiTest.php:95:test('storefront order api pays a checkout and exposes token-gated order lookup', function (): void {
tests/Feature/Api/StorefrontOrderApiTest.php:96:    [$checkoutId] = storefrontOrderApiCheckout($this);
tests/Feature/Api/StorefrontOrderApiTest.php:100:        ->postJson("/api/storefront/v1/checkouts/{$checkoutId}/pay", [
tests/Feature/Api/StorefrontOrderApiTest.php:117:        ->getJson('/api/storefront/v1/orders/%231001?token='.$token)
tests/Feature/Api/StorefrontOrderApiTest.php:123:        ->getJson('/api/storefront/v1/orders/%231001?token=bad-token')
tests/Feature/Api/StorefrontOrderApiTest.php:127:test('storefront order api returns payment failures and releases reservations', function (): void {
tests/Feature/Api/StorefrontOrderApiTest.php:128:    [$checkoutId, $variant] = storefrontOrderApiCheckout($this, '10.0.0.42');
tests/Feature/Api/StorefrontOrderApiTest.php:132:        ->postJson("/api/storefront/v1/checkouts/{$checkoutId}/pay", [
app/Livewire/Admin/Developers/Index.php:196:        $store = app('current_store');
app/Models/Scopes/StoreScope.php:14:        if (! app()->bound('current_store')) {
app/Models/Scopes/StoreScope.php:18:        $store = app('current_store');
tests/Feature/Checkout/PricingServicesTest.php:25:    app()->instance('current_store', $store);
tests/Feature/Catalog/CatalogFoundationTest.php:44:    app()->instance('current_store', $firstStore);
tests/Feature/Catalog/CatalogFoundationTest.php:64:    app()->instance('current_store', $firstStore);
tests/Feature/Catalog/CatalogFoundationTest.php:77:    app()->instance('current_store', $resolvedStore);
tests/Feature/Api/StorefrontCartApiTest.php:18:function storefrontApiCartStore(): Store
tests/Feature/Api/StorefrontCartApiTest.php:23:function storefrontApiCartVariant(Store $store): ProductVariant
tests/Feature/Api/StorefrontCartApiTest.php:45:test('storefront cart api creates and retrieves carts for the resolved store', function (): void {
tests/Feature/Api/StorefrontCartApiTest.php:46:    $store = storefrontApiCartStore();
tests/Feature/Api/StorefrontCartApiTest.php:49:        ->postJson('/api/storefront/v1/carts', ['currency' => $store->default_currency]);
tests/Feature/Api/StorefrontCartApiTest.php:61:        ->getJson("/api/storefront/v1/carts/{$response['data']['id']}")
tests/Feature/Api/StorefrontCartApiTest.php:67:test('storefront cart api adds updates and removes line items with version increments', function (): void {
tests/Feature/Api/StorefrontCartApiTest.php:68:    $store = storefrontApiCartStore();
tests/Feature/Api/StorefrontCartApiTest.php:69:    $variant = storefrontApiCartVariant($store);
tests/Feature/Api/StorefrontCartApiTest.php:72:        ->postJson('/api/storefront/v1/carts')
tests/Feature/Api/StorefrontCartApiTest.php:76:        ->postJson("/api/storefront/v1/carts/{$cartId}/lines", [
tests/Feature/Api/StorefrontCartApiTest.php:91:        ->putJson("/api/storefront/v1/carts/{$cartId}/lines/{$lineId}", [
tests/Feature/Api/StorefrontCartApiTest.php:101:        ->deleteJson("/api/storefront/v1/carts/{$cartId}/lines/{$lineId}", [
tests/Feature/Api/StorefrontCartApiTest.php:112:test('storefront cart api returns the current cart on version conflicts', function (): void {
tests/Feature/Api/StorefrontCartApiTest.php:113:    $store = storefrontApiCartStore();
tests/Feature/Api/StorefrontCartApiTest.php:114:    $variant = storefrontApiCartVariant($store);
tests/Feature/Api/StorefrontCartApiTest.php:117:        ->postJson('/api/storefront/v1/carts')
tests/Feature/Api/StorefrontCartApiTest.php:121:        ->postJson("/api/storefront/v1/carts/{$cartId}/lines", [
tests/Feature/Api/StorefrontCartApiTest.php:128:        ->putJson("/api/storefront/v1/carts/{$cartId}/lines/{$lineId}", [
tests/Feature/Api/StorefrontCartApiTest.php:139:test('storefront cart api validates tenant and request boundaries', function (): void {
tests/Feature/Api/StorefrontCartApiTest.php:140:    $store = storefrontApiCartStore();
tests/Feature/Api/StorefrontCartApiTest.php:143:        ->postJson('/api/storefront/v1/carts', ['currency' => 'USD'])
tests/Feature/Api/StorefrontCartApiTest.php:148:        ->getJson('/api/storefront/v1/carts/999999')
tests/Feature/Api/StorefrontCartApiTest.php:154:        ->getJson("/api/storefront/v1/carts/{$otherStoreCart->getKey()}")
tests/Feature/Checkout/CheckoutServiceTest.php:24:    app()->instance('current_store', $store);
tests/Feature/Catalog/InventoryServiceTest.php:14:    app()->instance('current_store', $store);
tests/Feature/Catalog/InventoryServiceTest.php:39:    app()->instance('current_store', $store);
tests/Feature/Catalog/VariantMatrixServiceTest.php:15:    app()->instance('current_store', $store);
tests/Feature/Catalog/VariantMatrixServiceTest.php:43:    app()->instance('current_store', $store);
tests/Feature/Catalog/VariantMatrixServiceTest.php:56:    app()->instance('current_store', $store);
tests/Feature/Cart/CartServiceTest.php:22:    app()->instance('current_store', $store);
app/Livewire/Admin/Analytics/Index.php:307:        $store = app('current_store');
tests/Feature/Foundation/TenancyTest.php:26:test('storefront requests resolve a store by hostname', function () {
tests/Feature/Foundation/TenancyTest.php:33:test('storefront requests reject unknown or suspended stores', function () {
tests/Feature/Foundation/TenancyTest.php:56:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Catalog/CatalogUiTest.php:22:test('storefront catalog browsing routes render seeded products', function () {
tests/Feature/Catalog/CatalogUiTest.php:78:    app()->instance('current_store', $store);
tests/Feature/Catalog/CatalogUiTest.php:158:    app()->instance('current_store', $store);
tests/Feature/Foundation/AuthorizationTest.php:34:    app()->instance('current_store', $store);
tests/Feature/Foundation/AuthorizationTest.php:50:    app()->instance('current_store', $store);
tests/Feature/Foundation/CustomerAuthTest.php:29:    app()->instance('current_store', $firstStore);
tests/Feature/Foundation/CustomerAuthTest.php:43:    app()->instance('current_store', $secondStore);
tests/Feature/Foundation/CustomerAuthTest.php:59:    app()->instance('current_store', $store);
tests/Feature/Foundation/CustomerAuthTest.php:74:    app()->instance('current_store', $store);
app/Http/Controllers/Api/Admin/V1/OrderRefundController.php:47:        app()->instance('current_store', $store);
app/Livewire/Admin/Themes/Editor.php:29:        $store = app('current_store');
app/Http/Controllers/Api/Admin/V1/OrderFulfillmentController.php:41:        app()->instance('current_store', $store);
tests/Feature/Admin/CustomerManagementTest.php:24:    app()->instance('current_store', $store);
tests/Feature/Admin/CustomerManagementTest.php:72:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/CustomerManagementTest.php:80:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/CustomerManagementTest.php:88:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/CustomerManagementTest.php:159:    app()->instance('current_store', $store);
app/Livewire/Admin/Themes/Index.php:26:        $store = app('current_store');
tests/Feature/Admin/OrderManagementTest.php:34:    app()->instance('current_store', $store);
tests/Feature/Admin/OrderManagementTest.php:225:    app()->instance('current_store', $store);
app/Http/Controllers/Api/Admin/V1/OrderController.php:64:        app()->instance('current_store', $store);
tests/Feature/Admin/ContentManagementTest.php:33:    app()->instance('current_store', $store);
tests/Feature/Admin/ContentManagementTest.php:66:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/ContentManagementTest.php:73:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/ContentManagementTest.php:79:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/ContentManagementTest.php:85:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/ContentManagementTest.php:91:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/DiscountManagementTest.php:31:    app()->instance('current_store', $store);
tests/Feature/Admin/DiscountManagementTest.php:77:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/DiscountManagementTest.php:86:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/DiscountManagementTest.php:92:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/DiscountManagementTest.php:195:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/DiscountManagementTest.php:200:        ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/DiscountManagementTest.php:205:        ->withSession(['current_store_id' => $store->getKey()])
app/Models/ShippingRate.php:40:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/ShippingRate.php:41:            if (! app()->bound('current_store')) {
app/Models/ShippingRate.php:45:            $store = app('current_store');
tests/Feature/Admin/DashboardTest.php:24:    app()->instance('current_store', $store);
tests/Feature/Admin/DashboardTest.php:80:        ->withSession(['current_store_id' => $store->getKey()])
app/Livewire/Admin/Apps/Show.php:70:        $store = app('current_store');
tests/Feature/Storefront/OrderViewsTest.php:33:    app()->instance('current_store', $store);
app/Http/Controllers/Api/Storefront/V1/CartController.php:31:        $store = app('current_store');
tests/Feature/Admin/SettingsManagementTest.php:30:    app()->instance('current_store', $store);
tests/Feature/Admin/SettingsManagementTest.php:58:            ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/SettingsManagementTest.php:63:            ->withSession(['current_store_id' => $store->getKey()])
tests/Feature/Admin/SettingsManagementTest.php:85:        ->set('newType', 'storefront')
tests/Feature/Storefront/CartCheckoutUiTest.php:29:function storefrontUiStore(): Store
tests/Feature/Storefront/CartCheckoutUiTest.php:32:    app()->instance('current_store', $store);
tests/Feature/Storefront/CartCheckoutUiTest.php:37:function storefrontUiVariant(Store $store): ProductVariant
tests/Feature/Storefront/CartCheckoutUiTest.php:51:    $store = storefrontUiStore();
tests/Feature/Storefront/CartCheckoutUiTest.php:52:    $variant = storefrontUiVariant($store);
tests/Feature/Storefront/CartCheckoutUiTest.php:66:    $store = storefrontUiStore();
tests/Feature/Storefront/CartCheckoutUiTest.php:67:    $variant = storefrontUiVariant($store);
tests/Feature/Storefront/CartCheckoutUiTest.php:87:    $store = storefrontUiStore();
tests/Feature/Storefront/CartCheckoutUiTest.php:88:    $variant = storefrontUiVariant($store);
tests/Feature/Storefront/CartCheckoutUiTest.php:119:    $store = storefrontUiStore();
tests/Feature/Storefront/CartCheckoutUiTest.php:120:    $variant = storefrontUiVariant($store);
tests/Feature/Storefront/CartCheckoutUiTest.php:143:    $store = storefrontUiStore();
tests/Feature/Storefront/CartCheckoutUiTest.php:144:    $variant = storefrontUiVariant($store);
tests/Feature/Admin/SearchSettingsTest.php:41:    app()->instance('current_store', $store);
tests/Feature/Admin/SearchSettingsTest.php:60:    app()->instance('current_store', $store);
app/Livewire/Admin/Apps/Index.php:65:        $store = app('current_store');
tests/Feature/Storefront/ThemeDataTest.php:52:    app()->instance('current_store', $firstStore);
tests/Feature/Storefront/ThemeDataTest.php:74:    app()->instance('current_store', $firstStore);
tests/Feature/Storefront/ThemeDataTest.php:108:test('storefront pages render published database content only', function () {
tests/Feature/Storefront/ThemeDataTest.php:127:        ->assertSee('Acme Fashion is the demo storefront');
tests/Feature/Admin/AppsDevelopersTest.php:60:    app()->instance('current_store', $store);
app/Models/CartLine.php:43:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/CartLine.php:44:            if (! app()->bound('current_store')) {
app/Models/CartLine.php:48:            $store = app('current_store');
tests/Feature/Admin/AnalyticsDashboardTest.php:58:    app()->instance('current_store', $store);
tests/Feature/Admin/AnalyticsDashboardTest.php:64:        ->set('channelFilter', 'storefront')
app/Http/Controllers/Api/Storefront/V1/OrderController.php:16:        $store = app('current_store');
app/Livewire/Admin/Products/Form.php:58:            $store = app('current_store');
app/Livewire/Admin/Products/Form.php:102:        $store = app('current_store');
app/Livewire/Admin/Pages/Form.php:33:        $store = app('current_store');
app/Livewire/Admin/Pages/Form.php:62:        $store = app('current_store');
app/Livewire/Admin/Pages/Form.php:111:        $store = app('current_store');
app/Http/Controllers/Api/Storefront/V1/AnalyticsEventController.php:22:        $store = app('current_store');
app/Models/ProductOptionValue.php:36:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/ProductOptionValue.php:37:            if (! app()->bound('current_store')) {
app/Models/ProductOptionValue.php:41:            $store = app('current_store');
app/Livewire/Admin/Pages/Index.php:27:        $store = app('current_store');
app/Livewire/Admin/Dashboard.php:74:        $store = app('current_store');
app/Models/ProductVariant.php:51:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/ProductVariant.php:52:            if (! app()->bound('current_store')) {
app/Models/ProductVariant.php:56:            $store = app('current_store');
app/Auth/CustomerUserProvider.php:36:        $store = app()->bound('current_store') ? app('current_store') : null;
app/Http/Controllers/Api/Storefront/V1/SearchController.php:56:        $store = app('current_store');
app/Models/NavigationItem.php:40:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/NavigationItem.php:41:            if (! app()->bound('current_store')) {
app/Models/NavigationItem.php:45:            $store = app('current_store');
app/Livewire/Admin/Collections/Form.php:36:            $store = app('current_store');
app/Livewire/Admin/Collections/Form.php:71:        $store = app('current_store');
app/Livewire/Admin/Settings/Shipping.php:276:        $store = app('current_store');
app/Traits/ChecksStoreRole.php:54:        if (! app()->bound('current_store')) {
app/Traits/ChecksStoreRole.php:58:        $store = app('current_store');
app/Models/ProductOption.php:36:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/ProductOption.php:37:            if (! app()->bound('current_store')) {
app/Models/ProductOption.php:41:            $store = app('current_store');
app/Livewire/Admin/Settings/Index.php:40:    public string $newType = 'storefront';
app/Livewire/Admin/Settings/Index.php:233:        $store = app('current_store');
app/Models/ThemeFile.php:37:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/ThemeFile.php:38:            if (! app()->bound('current_store')) {
app/Models/ThemeFile.php:42:            $store = app('current_store');
app/Services/ThemeSettingsService.php:63:                'tagline' => 'A self-contained demo storefront with scoped catalog data and checkout-ready products.',
app/Livewire/Admin/Orders/Show.php:47:        $store = app('current_store');
app/Livewire/Admin/Settings/Taxes.php:113:        $store = app('current_store');
app/Livewire/Admin/Orders/Index.php:39:        $store = app('current_store');
app/Models/ThemeSettings.php:31:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/ThemeSettings.php:32:            if (! app()->bound('current_store')) {
app/Models/ThemeSettings.php:36:            $store = app('current_store');
app/Models/ProductMedia.php:47:        static::addGlobalScope('current_store', function (Builder $builder): void {
app/Models/ProductMedia.php:48:            if (! app()->bound('current_store')) {
app/Models/ProductMedia.php:52:            $store = app('current_store');
app/Livewire/Admin/Customers/Show.php:45:        $store = app('current_store');
app/Providers/AppServiceProvider.php:95:        RateLimiter::for('api.storefront', function (Request $request): Limit {
app/Providers/AppServiceProvider.php:134:        View::composer('layouts.storefront', function (ViewInstance $view): void {
app/Providers/AppServiceProvider.php:135:            $store = app()->bound('current_store') ? app('current_store') : null;
app/Enums/StoreDomainType.php:7:    case Storefront = 'storefront';
app/Livewire/Admin/Customers/Index.php:26:        $store = app('current_store');
app/Livewire/Admin/Search/Settings.php:153:        $store = app('current_store');
app/Livewire/Storefront/Pages/Show.php:32:        return view('livewire.storefront.pages.show')
app/Livewire/Storefront/Pages/Show.php:33:            ->layout('layouts.storefront', [
app/Livewire/Admin/Discounts/Form.php:63:        $store = app('current_store');
app/Livewire/Admin/Discounts/Form.php:87:        $store = app('current_store');
app/Livewire/Admin/Discounts/Form.php:361:        $store = app('current_store');
app/Livewire/Storefront/Search/Index.php:95:        return view('livewire.storefront.search.index', [
app/Livewire/Storefront/Search/Index.php:99:        ])->layout('layouts.storefront', [
app/Livewire/Storefront/Search/Index.php:120:        $store = app('current_store');
app/Livewire/Storefront/Collections/Show.php:121:        return view('livewire.storefront.collections.show', [
app/Livewire/Storefront/Collections/Show.php:126:        ])->layout('layouts.storefront', [
app/Livewire/Admin/Discounts/Index.php:35:        $store = app('current_store');
app/Livewire/Storefront/Collections/Index.php:24:        return view('livewire.storefront.collections.index', [
app/Livewire/Storefront/Collections/Index.php:26:        ])->layout('layouts.storefront', [
app/Livewire/Storefront/Checkout/Confirmation.php:22:        $store = app('current_store');
app/Livewire/Storefront/Checkout/Confirmation.php:46:        return view('livewire.storefront.checkout.confirmation', [
app/Livewire/Storefront/Checkout/Confirmation.php:48:        ])->layout('layouts.storefront', [
app/Livewire/Admin/Navigation/Index.php:251:        $store = app('current_store');
app/Livewire/Storefront/Home.php:16:        $store = app('current_store');
app/Livewire/Storefront/Home.php:55:        return view('livewire.storefront.home', [
app/Livewire/Storefront/Home.php:60:        ])->layout('layouts.storefront', [
app/Livewire/Storefront/Products/Show.php:92:            app()->instance('current_store', $store);
app/Livewire/Storefront/Products/Show.php:97:        $store = app('current_store');
app/Livewire/Storefront/Products/Show.php:178:        return view('livewire.storefront.products.show', [
app/Livewire/Storefront/Products/Show.php:182:        ])->layout('layouts.storefront', [
app/Livewire/Storefront/Cart/Show.php:144:            app()->instance('current_store', $store);
app/Livewire/Storefront/Cart/Show.php:149:        $store = app('current_store');
app/Livewire/Storefront/Cart/Show.php:269:        return view('livewire.storefront.cart.show', [
app/Livewire/Storefront/Cart/Show.php:281:        ])->layout('layouts.storefront', [
app/Livewire/Storefront/Account/Orders/Show.php:22:        $store = app('current_store');
app/Livewire/Storefront/Account/Orders/Show.php:41:        return view('livewire.storefront.account.orders.show', [
app/Livewire/Storefront/Account/Orders/Show.php:43:        ])->layout('layouts.storefront', [
app/Livewire/Storefront/Checkout/Show.php:257:            app()->instance('current_store', $store);
app/Livewire/Storefront/Checkout/Show.php:262:        $store = app('current_store');
app/Livewire/Storefront/Checkout/Show.php:376:        return view('livewire.storefront.checkout.show', [
app/Livewire/Storefront/Checkout/Show.php:385:        ])->layout('layouts.storefront', [
app/Http/Middleware/ResolveStore.php:28:        app()->instance('current_store', $store);
app/Http/Middleware/ResolveStore.php:63:        $storeId = $request->session()->get('current_store_id');
app/Http/Middleware/ResolveStore.php:69:                $request->session()->put('current_store_id', $storeId);
app/Livewire/Storefront/Account/Auth/Register.php:27:        $store = app('current_store');
app/Livewire/Storefront/Account/Auth/Register.php:38:        app()->instance('current_store', $store);
app/Livewire/Storefront/Account/Auth/Register.php:71:        return view('livewire.storefront.account.auth.register')
app/Livewire/Storefront/CartDrawer.php:76:            app()->instance('current_store', $store);
app/Livewire/Storefront/CartDrawer.php:81:        $store = app('current_store');
app/Livewire/Storefront/CartDrawer.php:118:        return view('livewire.storefront.cart-drawer', [
app/Livewire/Storefront/Account/Auth/ForgotPassword.php:11:        return view('livewire.storefront.account.auth.forgot-password');
app/Livewire/Storefront/Account/Auth/ResetPassword.php:11:        return view('livewire.storefront.account.auth.reset-password');
app/Livewire/Storefront/Account/Auth/Login.php:25:        $store = app('current_store');
app/Livewire/Storefront/Account/Auth/Login.php:50:        app()->instance('current_store', Store::query()->findOrFail($this->storeId));
app/Livewire/Storefront/Account/Auth/Login.php:80:        return view('livewire.storefront.account.auth.login')
app/Livewire/Storefront/Account/Orders/Index.php:20:        $store = app('current_store');
app/Livewire/Storefront/Account/Orders/Index.php:29:        return view('livewire.storefront.account.orders.index', [
app/Livewire/Storefront/Account/Orders/Index.php:31:        ])->layout('layouts.storefront', [
app/Http/Requests/Api/Storefront/V1/StoreCartRequest.php:27:        $store = app()->bound('current_store') ? app('current_store') : null;
app/Http/Middleware/CheckStoreRole.php:20:        $store = app()->bound('current_store') ? app('current_store') : null;


cmd: sed -n '1,220p' resources/views/components/auth-header.blade.php


cmd: sed -n '1,260p' resources/views/layouts/auth.blade.php


cmd: sed -n '1,180p' app/Models/Concerns/BelongsToStore.php


cmd: sed -n '1,160p' app/Models/Scopes/StoreScope.php


cmd: sed -n '1,160p' app/Http/Middleware/ResolveStore.php


command: sed -n '1,220p' resources/views/components/auth-header.blade.php

auth-header.blade.php

type: read

aggregated_output:
@props([
    'title',
    'description',
])

<div class="flex w-full flex-col text-center">
    <flux:heading size="xl">{{ $title }}</flux:heading>
    <flux:subheading>{{ $description }}</flux:subheading>
</div>

status: completed


command:
/bin/zsh
-lc
sed -n '1,260p' tests/Pest.php

Pest.php

cmd: sed -n '1,260p' tests/Pest.php

aggregated_output:
<?php

/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
| need to change it using the "pest()" function to bind a different classes or traits.
|
*/

pest()->extend(Tests\TestCase::class)
 // ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
    ->in('Feature');

/*
|--------------------------------------------------------------------------
| Expectations
|--------------------------------------------------------------------------
|
| When you're writing tests, you often need to check that values meet certain conditions. The
| "expect()" function gives you access to a set of "expectations" methods that you can use
| to assert different things. Of course, you may extend the Expectation API at any time.
|
*/

expect()->extend('toBeOne', function () {
    return $this->toBe(1);
});

/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
| project that you don't want to repeat in every file. Here you can also expose helpers as
| global functions to help you to reduce the number of lines of code in your test files.
|
*/

function something()
{
    // ..
}


output:
Chunk ID: dace56
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 396
Output:
<?php

/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
| need to change it using the "pest()" function to bind a different classes or traits.
|
*/

pest()->extend(Tests\TestCase::class)
 // ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
    ->in('Feature');

/*
|--------------------------------------------------------------------------
| Expectations
|--------------------------------------------------------------------------
|
| When you're writing tests, you often need to check that values meet certain conditions. The
| "expect()" function gives you access to a set of "expectations" methods that you can use
| to assert different things. Of course, you may extend the Expectation API at any time.
|
*/

expect()->extend('toBeOne', function () {
    return $this->toBe(1);
});

/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
| project that you don't want to repeat in every file. Here you can also expose helpers as
| global functions to help you to reduce the number of lines of code in your test files.
|
*/

function something()
{
    // ..
}


output:
Chunk ID: 7707ec
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 54
Output:
@props([
    'title',
    'description',
])

<div class="flex w-full flex-col text-center">
    <flux:heading size="xl">{{ $title }}</flux:heading>
    <flux:subheading>{{ $description }}</flux:subheading>
</div>


command: sed -n '1,260p' resources/views/layouts/auth.blade.php

aggregated_output:
<x-layouts::auth.simple :title="$title ?? null">
    {{ $slot }}
</x-layouts::auth.simple>


output:
Chunk ID: 53677d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 23
Output:
<x-layouts::auth.simple :title="$title ?? null">
    {{ $slot }}
</x-layouts::auth.simple>


command: sed -n '1,160p' app/Models/Scopes/StoreScope.php

aggregated_output:
<?php

namespace App\Models\Scopes;

use App\Models\Store;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class StoreScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        if (! app()->bound('current_store')) {
            return;
        }

        $store = app('current_store');

        if (! $store instanceof Store) {
            return;
        }

        $builder->where($model->qualifyColumn('store_id'), $store->getKey());
    }
}


command: sed -n '1,180p' app/Models/Concerns/BelongsToStore.php

aggregated_output:
<?php

namespace App\Models\Concerns;

use App\Models\Scopes\StoreScope;
use App\Models\Store;

trait BelongsToStore
{
    public static function bootBelongsToStore(): void
    {
        static::addGlobalScope(new StoreScope);

        static::creating(function (object $model): void {
            if ($model->store_id || ! app()->bound('current_store')) {
                return;
            }

            $store = app('current_store');

            if ($store instanceof Store) {
                $model->store_id = $store->getKey();
            }
        });
    }
}


output:
Chunk ID: 35d25c
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 143
Output:
<?php

namespace App\Models\Concerns;

use App\Models\Scopes\StoreScope;
use App\Models\Store;

trait BelongsToStore
{
    public static function bootBelongsToStore(): void
    {
        static::addGlobalScope(new StoreScope);

        static::creating(function (object $model): void {
            if ($model->store_id || ! app()->bound('current_store')) {
                return;
            }

            $store = app('current_store');

            if ($store instanceof Store) {
                $model->store_id = $store->getKey();
            }
        });
    }
}


output:
Chunk ID: 73088f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 141
Output:
<?php

namespace App\Models\Scopes;

use App\Models\Store;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class StoreScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        if (! app()->bound('current_store')) {
            return;
        }

        $store = app('current_store');

        if (! $store instanceof Store) {
            return;
        }

        $builder->where($model->qualifyColumn('store_id'), $store->getKey());
    }
}


command: sed -n '1,160p' app/Http/Middleware/ResolveStore.php

aggregated_output:
<?php

namespace App\Http\Middleware;

use App\Enums\StoreStatus;
use App\Models\Store;
use App\Models\StoreDomain;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;

class ResolveStore
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $store = $request->is('admin*')
            ? $this->resolveAdminStore($request)
            : $this->resolveStorefrontStore($request);

        abort_unless($store, 404);

        app()->instance('current_store', $store);

        if (! $request->is('admin*') && $store->status === StoreStatus::Suspended) {
            abort(503);
        }

        if ($request->is('admin*') && $store->status === StoreStatus::Suspended && ! $request->isMethodSafe()) {
            abort(403);
        }

        return $next($request);
    }

    private function resolveStorefrontStore(Request $request): ?Store
    {
        $hostname = $request->getHost();
        $cacheKey = "store_domain:{$hostname}";

        $storeId = Cache::remember($cacheKey, now()->addMinutes(5), function () use ($hostname): ?int {
            return StoreDomain::query()
                ->where('hostname', $hostname)
                ->value('store_id');
        });

        return $storeId ? Store::query()->find($storeId) : null;
    }

    private function resolveAdminStore(Request $request): ?Store
    {
        $user = $request->user();

        if (! $user) {
            return null;
        }

        $storeId = $request->session()->get('current_store_id');

        if (! $storeId) {
            $storeId = $user->stores()->oldest('stores.id')->value('stores.id');

            if ($storeId) {
                $request->session()->put('current_store_id', $storeId);
            }
        }

        if (! $storeId || ! $user->stores()->whereKey($storeId)->exists()) {
            return null;
        }

        return Store::query()->find($storeId);
    }
}


output:
Chunk ID: 0b79cf
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 538
Output:
<?php

namespace App\Http\Middleware;

use App\Enums\StoreStatus;
use App\Models\Store;
use App\Models\StoreDomain;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;

class ResolveStore
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $store = $request->is('admin*')
            ? $this->resolveAdminStore($request)
            : $this->resolveStorefrontStore($request);

        abort_unless($store, 404);

        app()->instance('current_store', $store);

        if (! $request->is('admin*') && $store->status === StoreStatus::Suspended) {
            abort(503);
        }

        if ($request->is('admin*') && $store->status === StoreStatus::Suspended && ! $request->isMethodSafe()) {
            abort(403);
        }

        return $next($request);
    }

    private function resolveStorefrontStore(Request $request): ?Store
    {
        $hostname = $request->getHost();
        $cacheKey = "store_domain:{$hostname}";

        $storeId = Cache::remember($cacheKey, now()->addMinutes(5), function () use ($hostname): ?int {
            return StoreDomain::query()
                ->where('hostname', $hostname)
                ->value('store_id');
        });

        return $storeId ? Store::query()->find($storeId) : null;
    }

    private function resolveAdminStore(Request $request): ?Store
    {
        $user = $request->user();

        if (! $user) {
            return null;
        }

        $storeId = $request->session()->get('current_store_id');

        if (! $storeId) {
            $storeId = $user->stores()->oldest('stores.id')->value('stores.id');

            if ($storeId) {
                $request->session()->put('current_store_id', $storeId);
            }
        }

        if (! $storeId || ! $user->stores()->whereKey($storeId)->exists()) {
            return null;
        }

        return Store::query()->find($storeId);
    }
}


cmd: sed -n '1,140p' database/factories/StoreDomainFactory.php


cmd: sed -n '1,180p' tests/Feature/Foundation/CustomerAuthTest.php


cmd: sed -n '1,140p' tests/Feature/Catalog/CatalogUiTest.php


cmd: sed -n '1,180p' app/Auth/CustomerUserProvider.php


command: sed -n '1,180p' tests/Feature/Foundation/CustomerAuthTest.php

aggregated_output:
<?php

use App\Livewire\Storefront\Account\Auth\Login as CustomerLogin;
use App\Livewire\Storefront\Account\Auth\Register as CustomerRegister;
use App\Models\Customer;
use App\Models\Store;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Auth;
use Livewire\Livewire;

uses(RefreshDatabase::class);

test('customer authentication is scoped to the current store', function () {
    $firstStore = Store::factory()->create();
    $secondStore = Store::factory()->create();

    Customer::factory()->create([
        'store_id' => $firstStore->getKey(),
        'email' => 'same@example.test',
        'password' => 'first-password',
    ]);

    Customer::factory()->create([
        'store_id' => $secondStore->getKey(),
        'email' => 'same@example.test',
        'password' => 'second-password',
    ]);

    app()->instance('current_store', $firstStore);

    expect(Auth::guard('customer')->attempt([
        'email' => 'same@example.test',
        'password' => 'first-password',
    ]))->toBeTrue();

    Auth::guard('customer')->logout();

    expect(Auth::guard('customer')->attempt([
        'email' => 'same@example.test',
        'password' => 'second-password',
    ]))->toBeFalse();

    app()->instance('current_store', $secondStore);

    expect(Auth::guard('customer')->attempt([
        'email' => 'same@example.test',
        'password' => 'second-password',
    ]))->toBeTrue();
});

test('customer login component authenticates within the resolved store', function () {
    $store = Store::factory()->create();
    $customer = Customer::factory()->create([
        'store_id' => $store->getKey(),
        'email' => 'customer@example.test',
        'password' => 'password',
    ]);

    app()->instance('current_store', $store);

    Livewire::test(CustomerLogin::class)
        ->set('email', $customer->email)
        ->set('password', 'password')
        ->call('login')
        ->assertHasNoErrors()
        ->assertRedirect(route('account.dashboard', absolute: false));

    $this->assertAuthenticatedAs($customer, 'customer');
});

test('customer registration is unique per store', function () {
    $store = Store::factory()->create();

    app()->instance('current_store', $store);

    Livewire::test(CustomerRegister::class)
        ->set('name', 'Jane Doe')
        ->set('email', 'jane@example.test')
        ->set('password', 'password')
        ->set('password_confirmation', 'password')
        ->set('marketing_opt_in', true)
        ->call('register')
        ->assertHasNoErrors()
        ->assertRedirect(route('account.dashboard', absolute: false));

    expect(Customer::withoutGlobalScopes()
        ->where('store_id', $store->getKey())
        ->where('email', 'jane@example.test')
        ->exists())->toBeTrue();

    Livewire::test(CustomerRegister::class)
        ->set('name', 'Jane Doe')
        ->set('email', 'jane@example.test')
        ->set('password', 'password')
        ->set('password_confirmation', 'password')
        ->call('register')
        ->assertHasErrors(['email']);
});


command: sed -n '1,140p' database/factories/StoreDomainFactory.php

aggregated_output:
<?php

namespace Database\Factories;

use App\Enums\StoreDomainType;
use App\Models\Store;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\StoreDomain>
 */
class StoreDomainFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'store_id' => Store::factory(),
            'hostname' => fake()->unique()->domainName(),
            'type' => StoreDomainType::Storefront,
            'is_primary' => true,
            'tls_mode' => 'managed',
        ];
    }

    public function admin(): static
    {
        return $this->state(fn (array $attributes): array => [
            'type' => StoreDomainType::Admin,
        ]);
    }

    public function api(): static
    {
        return $this->state(fn (array $attributes): array => [
            'type' => StoreDomainType::Api,
        ]);
    }

    public function secondary(): static
    {
        return $this->state(fn (array $attributes): array => [
            'is_primary' => false,
        ]);
    }
}


command: sed -n '1,140p' tests/Feature/Catalog/CatalogUiTest.php

aggregated_output:
<?php

use App\Livewire\Admin\Collections\Form as CollectionForm;
use App\Livewire\Admin\Products\Form as ProductForm;
use App\Livewire\Admin\Products\Index as ProductIndex;
use App\Models\Collection;
use App\Models\Product;
use App\Models\Store;
use App\Models\User;
use App\Services\ProductService;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->withoutVite();
    $this->seed(DatabaseSeeder::class);
});

test('storefront catalog browsing routes render seeded products', function () {
    $this->withHeader('Host', 'shop.test')
        ->get('/')
        ->assertSuccessful()
        ->assertSee('Acme Fashion')
        ->assertSee('Classic Cotton T-Shirt')
        ->assertSee('24.99 EUR');

    $this->withHeader('Host', 'shop.test')
        ->get('/collections/t-shirts')
        ->assertSuccessful()
        ->assertSee('T-Shirts')
        ->assertSee('Classic Cotton T-Shirt')
        ->assertDontSee('Unreleased Winter Jacket');

    $this->withHeader('Host', 'shop.test')
        ->get('/products/classic-cotton-t-shirt')
        ->assertSuccessful()
        ->assertSee('Classic Cotton T-Shirt')
        ->assertSee('Size')
        ->assertSee('Color')
        ->assertSee('Add to cart');

    $this->withHeader('Host', 'shop.test')
        ->get('/search?q=draft')
        ->assertSuccessful()
        ->assertDontSee('Unreleased Winter Jacket');
});

test('admin catalog routes require authentication and render for store admins', function () {
    $this->get('/admin/products')->assertRedirect('/admin/login');

    $user = User::query()->where('email', 'admin@acme.test')->firstOrFail();

    $this->actingAs($user)
        ->get('/admin/products')
        ->assertSuccessful()
        ->assertSee('Classic Cotton T-Shirt')
        ->assertSee('Premium Slim Fit Jeans');

    $this->actingAs($user)
        ->get('/admin/collections')
        ->assertSuccessful()
        ->assertSee('Collections')
        ->assertSee('T-Shirts');

    $this->actingAs($user)
        ->get('/admin/inventory')
        ->assertSuccessful()
        ->assertSee('Inventory')
        ->assertSee('Limited Edition Sneakers');
});

test('admin product form creates edits archives and index filters products', function () {
    $store = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    $user = User::query()->where('email', 'admin@acme.test')->firstOrFail();
    app()->instance('current_store', $store);

    Livewire::actingAs($user)
        ->test(ProductForm::class)
        ->set('title', 'Test Product Created by E2E')
        ->set('handle', 'test-product-created-by-e2e')
        ->set('descriptionHtml', 'This product was created by the E2E test suite.')
        ->set('vendor', 'Test Vendor')
        ->set('productType', 'T-Shirts')
        ->set('variants.0.price', '29.99')
        ->set('variants.0.sku', 'E2E-TEST-001')
        ->set('variants.0.quantity', 50)
        ->call('save')
        ->assertSee('Product saved');

    $product = Product::query()->where('handle', 'test-product-created-by-e2e')->firstOrFail();

    expect($product->variants()->first()?->price_amount)->toBe(2999)
        ->and($product->variants()->first()?->inventoryItem?->quantity_on_hand)->toBe(50);

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $product])
        ->set('title', 'Test Product Updated')
        ->call('save')
        ->assertSee('Product saved');

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $product->refresh()])
        ->set('variants.0.sku', 'ACME-CTSH-S-WHT')
        ->call('save')
        ->assertHasErrors(['variants.0.sku']);

    expect($product->refresh()->variants()->first()?->sku)->toBe('E2E-TEST-001');

    $draftProduct = app(ProductService::class)->create($store, [
        'title' => 'Draft Product With Price Later',
        'handle' => 'draft-product-with-price-later',
        'status' => 'draft',
        'variants' => [[
            'sku' => 'DRAFT-PRICE-LATER-001',
            'price_amount' => 0,
            'currency' => $store->default_currency,
            'quantity_on_hand' => 4,
            'is_default' => true,
            'position' => 0,
        ]],
    ]);

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $draftProduct])
        ->set('variants.0.price', '17.50')
        ->set('status', 'active')
        ->call('save')
        ->assertSee('Product saved');

    expect($draftProduct->refresh()->status->value)->toBe('active')
        ->and($draftProduct->variants()->first()?->price_amount)->toBe(1750);

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $product->refresh()])
        ->set('status', 'archived')
        ->call('save')
        ->assertSee('Product saved');


output:
Chunk ID: 5bca2b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 297
Output:
<?php

namespace Database\Factories;

use App\Enums\StoreDomainType;
use App\Models\Store;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\StoreDomain>
 */
class StoreDomainFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'store_id' => Store::factory(),
            'hostname' => fake()->unique()->domainName(),
            'type' => StoreDomainType::Storefront,
            'is_primary' => true,
            'tls_mode' => 'managed',
        ];
    }

    public function admin(): static
    {
        return $this->state(fn (array $attributes): array => [
            'type' => StoreDomainType::Admin,
        ]);
    }

    public function api(): static
    {
        return $this->state(fn (array $attributes): array => [
            'type' => StoreDomainType::Api,
        ]);
    }

    public function secondary(): static
    {
        return $this->state(fn (array $attributes): array => [
            'is_primary' => false,
        ]);
    }
}


output:
Chunk ID: 59aded
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 769
Output:
<?php

use App\Livewire\Storefront\Account\Auth\Login as CustomerLogin;
use App\Livewire\Storefront\Account\Auth\Register as CustomerRegister;
use App\Models\Customer;
use App\Models\Store;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Auth;
use Livewire\Livewire;

uses(RefreshDatabase::class);

test('customer authentication is scoped to the current store', function () {
    $firstStore = Store::factory()->create();
    $secondStore = Store::factory()->create();

    Customer::factory()->create([
        'store_id' => $firstStore->getKey(),
        'email' => 'same@example.test',
        'password' => 'first-password',
    ]);

    Customer::factory()->create([
        'store_id' => $secondStore->getKey(),
        'email' => 'same@example.test',
        'password' => 'second-password',
    ]);

    app()->instance('current_store', $firstStore);

    expect(Auth::guard('customer')->attempt([
        'email' => 'same@example.test',
        'password' => 'first-password',
    ]))->toBeTrue();

    Auth::guard('customer')->logout();

    expect(Auth::guard('customer')->attempt([
        'email' => 'same@example.test',
        'password' => 'second-password',
    ]))->toBeFalse();

    app()->instance('current_store', $secondStore);

    expect(Auth::guard('customer')->attempt([
        'email' => 'same@example.test',
        'password' => 'second-password',
    ]))->toBeTrue();
});

test('customer login component authenticates within the resolved store', function () {
    $store = Store::factory()->create();
    $customer = Customer::factory()->create([
        'store_id' => $store->getKey(),
        'email' => 'customer@example.test',
        'password' => 'password',
    ]);

    app()->instance('current_store', $store);

    Livewire::test(CustomerLogin::class)
        ->set('email', $customer->email)
        ->set('password', 'password')
        ->call('login')
        ->assertHasNoErrors()
        ->assertRedirect(route('account.dashboard', absolute: false));

    $this->assertAuthenticatedAs($customer, 'customer');
});

test('customer registration is unique per store', function () {
    $store = Store::factory()->create();

    app()->instance('current_store', $store);

    Livewire::test(CustomerRegister::class)
        ->set('name', 'Jane Doe')
        ->set('email', 'jane@example.test')
        ->set('password', 'password')
        ->set('password_confirmation', 'password')
        ->set('marketing_opt_in', true)
        ->call('register')
        ->assertHasNoErrors()
        ->assertRedirect(route('account.dashboard', absolute: false));

    expect(Customer::withoutGlobalScopes()
        ->where('store_id', $store->getKey())
        ->where('email', 'jane@example.test')
        ->exists())->toBeTrue();

    Livewire::test(CustomerRegister::class)
        ->set('name', 'Jane Doe')
        ->set('email', 'jane@example.test')
        ->set('password', 'password')
        ->set('password_confirmation', 'password')
        ->call('register')
        ->assertHasErrors(['email']);
});


output:
Chunk ID: 72e24e
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1226
Output:
<?php

use App\Livewire\Admin\Collections\Form as CollectionForm;
use App\Livewire\Admin\Products\Form as ProductForm;
use App\Livewire\Admin\Products\Index as ProductIndex;
use App\Models\Collection;
use App\Models\Product;
use App\Models\Store;
use App\Models\User;
use App\Services\ProductService;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->withoutVite();
    $this->seed(DatabaseSeeder::class);
});

test('storefront catalog browsing routes render seeded products', function () {
    $this->withHeader('Host', 'shop.test')
        ->get('/')
        ->assertSuccessful()
        ->assertSee('Acme Fashion')
        ->assertSee('Classic Cotton T-Shirt')
        ->assertSee('24.99 EUR');

    $this->withHeader('Host', 'shop.test')
        ->get('/collections/t-shirts')
        ->assertSuccessful()
        ->assertSee('T-Shirts')
        ->assertSee('Classic Cotton T-Shirt')
        ->assertDontSee('Unreleased Winter Jacket');

    $this->withHeader('Host', 'shop.test')
        ->get('/products/classic-cotton-t-shirt')
        ->assertSuccessful()
        ->assertSee('Classic Cotton T-Shirt')
        ->assertSee('Size')
        ->assertSee('Color')
        ->assertSee('Add to cart');

    $this->withHeader('Host', 'shop.test')
        ->get('/search?q=draft')
        ->assertSuccessful()
        ->assertDontSee('Unreleased Winter Jacket');
});

test('admin catalog routes require authentication and render for store admins', function () {
    $this->get('/admin/products')->assertRedirect('/admin/login');

    $user = User::query()->where('email', 'admin@acme.test')->firstOrFail();

    $this->actingAs($user)
        ->get('/admin/products')
        ->assertSuccessful()
        ->assertSee('Classic Cotton T-Shirt')
        ->assertSee('Premium Slim Fit Jeans');

    $this->actingAs($user)
        ->get('/admin/collections')
        ->assertSuccessful()
        ->assertSee('Collections')
        ->assertSee('T-Shirts');

    $this->actingAs($user)
        ->get('/admin/inventory')
        ->assertSuccessful()
        ->assertSee('Inventory')
        ->assertSee('Limited Edition Sneakers');
});

test('admin product form creates edits archives and index filters products', function () {
    $store = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    $user = User::query()->where('email', 'admin@acme.test')->firstOrFail();
    app()->instance('current_store', $store);

    Livewire::actingAs($user)
        ->test(ProductForm::class)
        ->set('title', 'Test Product Created by E2E')
        ->set('handle', 'test-product-created-by-e2e')
        ->set('descriptionHtml', 'This product was created by the E2E test suite.')
        ->set('vendor', 'Test Vendor')
        ->set('productType', 'T-Shirts')
        ->set('variants.0.price', '29.99')
        ->set('variants.0.sku', 'E2E-TEST-001')
        ->set('variants.0.quantity', 50)
        ->call('save')
        ->assertSee('Product saved');

    $product = Product::query()->where('handle', 'test-product-created-by-e2e')->firstOrFail();

    expect($product->variants()->first()?->price_amount)->toBe(2999)
        ->and($product->variants()->first()?->inventoryItem?->quantity_on_hand)->toBe(50);

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $product])
        ->set('title', 'Test Product Updated')
        ->call('save')
        ->assertSee('Product saved');

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $product->refresh()])
        ->set('variants.0.sku', 'ACME-CTSH-S-WHT')
        ->call('save')
        ->assertHasErrors(['variants.0.sku']);

    expect($product->refresh()->variants()->first()?->sku)->toBe('E2E-TEST-001');

    $draftProduct = app(ProductService::class)->create($store, [
        'title' => 'Draft Product With Price Later',
        'handle' => 'draft-product-with-price-later',
        'status' => 'draft',
        'variants' => [[
            'sku' => 'DRAFT-PRICE-LATER-001',
            'price_amount' => 0,
            'currency' => $store->default_currency,
            'quantity_on_hand' => 4,
            'is_default' => true,
            'position' => 0,
        ]],
    ]);

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $draftProduct])
        ->set('variants.0.price', '17.50')
        ->set('status', 'active')
        ->call('save')
        ->assertSee('Product saved');

    expect($draftProduct->refresh()->status->value)->toBe('active')
        ->and($draftProduct->variants()->first()?->price_amount)->toBe(1750);

    Livewire::actingAs($user)
        ->test(ProductForm::class, ['product' => $product->refresh()])
        ->set('status', 'archived')
        ->call('save')
        ->assertSee('Product saved');


command: sed -n '1,180p' app/Auth/CustomerUserProvider.php

aggregated_output:
<?php

namespace App\Auth;

use App\Models\Store;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable;

class CustomerUserProvider extends EloquentUserProvider
{
    /**
     * @param  array<string, mixed>  $credentials
     */
    public function retrieveByCredentials(array $credentials): ?Authenticatable
    {
        $credentials = array_filter(
            $credentials,
            fn (mixed $value, string $key): bool => ! str_contains($key, 'password') && $value !== null,
            ARRAY_FILTER_USE_BOTH,
        );

        if ($credentials === []) {
            return null;
        }

        $query = $this->newModelQuery();

        foreach ($credentials as $key => $value) {
            if (is_array($value) || $value instanceof \Closure) {
                continue;
            }

            $query->where($key, $value);
        }

        $store = app()->bound('current_store') ? app('current_store') : null;

        if (! $store instanceof Store) {
            return null;
        }

        $query->where('store_id', $store->getKey());

        return $query->first();
    }
}


output:
Chunk ID: 376dd5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 285
Output:
<?php

namespace App\Auth;

use App\Models\Store;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable;

class CustomerUserProvider extends EloquentUserProvider
{
    /**
     * @param  array<string, mixed>  $credentials
     */
    public function retrieveByCredentials(array $credentials): ?Authenticatable
    {
        $credentials = array_filter(
            $credentials,
            fn (mixed $value, string $key): bool => ! str_contains($key, 'password') && $value !== null,
            ARRAY_FILTER_USE_BOTH,
        );

        if ($credentials === []) {
            return null;
        }

        $query = $this->newModelQuery();

        foreach ($credentials as $key => $value) {
            if (is_array($value) || $value instanceof \Closure) {
                continue;
            }

            $query->where($key, $value);
        }

        $store = app()->bound('current_store') ? app('current_store') : null;

        if (! $store instanceof Store) {
            return null;
        }

        $query->where('store_id', $store->getKey());

        return $query->first();
    }
}


command:
/bin/zsh
-lc
sed -n '1,220p' config/auth.php

auth.php

cmd: sed -n '1,220p' config/auth.php

aggregated_output:
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option defines the default authentication "guard" and password
    | reset "broker" for your application. You may change these values
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => env('AUTH_GUARD', 'web'),
        'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | which utilizes session storage plus the Eloquent user provider.
    |
    | All authentication guards have a user provider, which defines how the
    | users are actually retrieved out of your database or other storage
    | system used by the application. Typically, Eloquent is utilized.
    |
    | Supported: "session"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'customer' => [
            'driver' => 'session',
            'provider' => 'customers',
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication guards have a user provider, which defines how the
    | users are actually retrieved out of your database or other storage
    | system used by the application. Typically, Eloquent is utilized.
    |
    | If you have multiple user tables or models you may configure multiple
    | providers to represent the model / table. These providers may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],

        'customers' => [
            'driver' => 'store_scoped_eloquent',
            'model' => App\Models\Customer::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | These configuration options specify the behavior of Laravel's password
    | reset functionality, including the table utilized for token storage
    | and the user provider that is invoked to actually retrieve users.
    |
    | The expiry time is the number of minutes that each reset token will be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    | The throttle setting is the number of seconds a user must wait before
    | generating more password reset tokens. This prevents the user from
    | quickly generating a very large amount of password reset tokens.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
            'expire' => 60,
            'throttle' => 60,
        ],

        'customers' => [
            'provider' => 'customers',
            'table' => 'customer_password_reset_tokens',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Password Confirmation Timeout
    |--------------------------------------------------------------------------
    |
    | Here you may define the number of seconds before a password confirmation
    | window expires and users are asked to re-enter their password via the
    | confirmation screen. By default, the timeout lasts for three hours.
    |
    */

    'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),

];


output:
Chunk ID: e996d7
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1117
Output:
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option defines the default authentication "guard" and password
    | reset "broker" for your application. You may change these values
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => env('AUTH_GUARD', 'web'),
        'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | which utilizes session storage plus the Eloquent user provider.
    |
    | All authentication guards have a user provider, which defines how the
    | users are actually retrieved out of your database or other storage
    | system used by the application. Typically, Eloquent is utilized.
    |
    | Supported: "session"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'customer' => [
            'driver' => 'session',
            'provider' => 'customers',
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication guards have a user provider, which defines how the
    | users are actually retrieved out of your database or other storage
    | system used by the application. Typically, Eloquent is utilized.
    |
    | If you have multiple user tables or models you may configure multiple
    | providers to represent the model / table. These providers may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],

        'customers' => [
            'driver' => 'store_scoped_eloquent',
            'model' => App\Models\Customer::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | These configuration options specify the behavior of Laravel's password
    | reset functionality, including the table utilized for token storage
    | and the user provider that is invoked to actually retrieve users.
    |
    | The expiry time is the number of minutes that each reset token will be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    | The throttle setting is the number of seconds a user must wait before
    | generating more password reset tokens. This prevents the user from
    | quickly generating a very large amount of password reset tokens.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
            'expire' => 60,
            'throttle' => 60,
        ],

        'customers' => [
            'provider' => 'customers',
            'table' => 'customer_password_reset_tokens',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Password Confirmation Timeout
    |--------------------------------------------------------------------------
    |
    | Here you may define the number of seconds before a password confirmation
    | window expires and users are asked to re-enter their password via the
    | confirmation screen. By default, the timeout lasts for three hours.
    |
    */

    'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),

];


cmd: sed -n '1,120p' vendor/laravel/framework/src/Illuminate/Auth/Events/PasswordReset.php


command: sed -n '1,120p' vendor/laravel/framework/src/Illuminate/Auth/Events/PasswordReset.php

aggregated_output:
<?php

namespace Illuminate\Auth\Events;

use Illuminate\Queue\SerializesModels;

class PasswordReset
{
    use SerializesModels;

    /**
     * Create a new event instance.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user  The user.
     */
    public function __construct(
        public $user,
    ) {
    }
}


output:
Chunk ID: 6e2d9b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 85
Output:
<?php

namespace Illuminate\Auth\Events;

use Illuminate\Queue\SerializesModels;

class PasswordReset
{
    use SerializesModels;

    /**
     * Create a new event instance.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user  The user.
     */
    public function __construct(
        public $user,
    ) {
    }
}


cmd: rg "class Locked|Attribute\\Locked|namespace Livewire\\Attributes" vendor/livewire -n | head -40


command: rg "class Locked|Attribute\\Locked|namespace Livewire\\Attributes" vendor/livewire -n | head -40

type: search

cmd: rg "class Locked|Attribute\\\\Locked|namespace Livewire\\\\Attributes" vendor/livewire -n

query: class Locked|Attribute\\Locked|namespace Livewire\\Attributes

path: livewire

aggregated_output:
rg: regex parse error:
    (?:class Locked|Attribute\Locked|namespace Livewire\Attributes)
                             ^^
error: unrecognized escape sequence


output:
Chunk ID: 87a101
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 40
Output:
rg: regex parse error:
    (?:class Locked|Attribute\Locked|namespace Livewire\Attributes)
                             ^^
error: unrecognized escape sequence


cmd: rg "class Locked|namespace Livewire\\Attributes" vendor/livewire -n | head -40


command: rg "class Locked|namespace Livewire\\Attributes" vendor/livewire -n | head -40

type: search

cmd: rg "class Locked|namespace Livewire\\\\Attributes" vendor/livewire -n

query: class Locked|namespace Livewire\\Attributes

path: livewire

aggregated_output: vendor/livewire/livewire/src/Attributes/Locked.php:8:class Locked extends BaseLocked


output:
Chunk ID: 9844d9
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 22
Output:
vendor/livewire/livewire/src/Attributes/Locked.php:8:class Locked extends BaseLocked


command:
/bin/zsh
-lc
sed -n '1,240p' app/Models/Store.php

Store.php

cmd: sed -n '1,240p' app/Models/Store.php

aggregated_output:
<?php

namespace App\Models;

use App\Enums\StoreStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Store extends Model
{
    /** @use HasFactory<\Database\Factories\StoreFactory> */
    use HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'organization_id',
        'name',
        'handle',
        'status',
        'default_currency',
        'default_locale',
        'timezone',
    ];

    /**
     * @return BelongsTo<Organization, $this>
     */
    public function organization(): BelongsTo
    {
        return $this->belongsTo(Organization::class);
    }

    /**
     * @return HasMany<StoreDomain, $this>
     */
    public function domains(): HasMany
    {
        return $this->hasMany(StoreDomain::class);
    }

    /**
     * @return BelongsToMany<User, $this>
     */
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'store_users')
            ->using(StoreUser::class)
            ->withPivot('role');
    }

    /**
     * @return HasOne<StoreSettings, $this>
     */
    public function settings(): HasOne
    {
        return $this->hasOne(StoreSettings::class);
    }

    /**
     * @return HasMany<Theme, $this>
     */
    public function themes(): HasMany
    {
        return $this->hasMany(Theme::class);
    }

    /**
     * @return HasMany<Page, $this>
     */
    public function pages(): HasMany
    {
        return $this->hasMany(Page::class);
    }

    /**
     * @return HasMany<NavigationMenu, $this>
     */
    public function navigationMenus(): HasMany
    {
        return $this->hasMany(NavigationMenu::class);
    }

    /**
     * @return HasMany<Cart, $this>
     */
    public function carts(): HasMany
    {
        return $this->hasMany(Cart::class);
    }

    /**
     * @return HasMany<Checkout, $this>
     */
    public function checkouts(): HasMany
    {
        return $this->hasMany(Checkout::class);
    }

    /**
     * @return HasMany<ShippingZone, $this>
     */
    public function shippingZones(): HasMany
    {
        return $this->hasMany(ShippingZone::class);
    }

    /**
     * @return HasOne<TaxSettings, $this>
     */
    public function taxSettings(): HasOne
    {
        return $this->hasOne(TaxSettings::class);
    }

    /**
     * @return HasMany<Discount, $this>
     */
    public function discounts(): HasMany
    {
        return $this->hasMany(Discount::class);
    }

    /**
     * @return HasMany<Order, $this>
     */
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }

    /**
     * @return HasMany<AppInstallation, $this>
     */
    public function appInstallations(): HasMany
    {
        return $this->hasMany(AppInstallation::class);
    }

    /**
     * @return HasMany<WebhookSubscription, $this>
     */
    public function webhookSubscriptions(): HasMany
    {
        return $this->hasMany(WebhookSubscription::class);
    }

    public function isSuspended(): bool
    {
        return $this->status === StoreStatus::Suspended;
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'status' => StoreStatus::class,
        ];
    }
}


output:
Chunk ID: a325df
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 883
Output:
<?php

namespace App\Models;

use App\Enums\StoreStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Store extends Model
{
    /** @use HasFactory<\Database\Factories\StoreFactory> */
    use HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'organization_id',
        'name',
        'handle',
        'status',
        'default_currency',
        'default_locale',
        'timezone',
    ];

    /**
     * @return BelongsTo<Organization, $this>
     */
    public function organization(): BelongsTo
    {
        return $this->belongsTo(Organization::class);
    }

    /**
     * @return HasMany<StoreDomain, $this>
     */
    public function domains(): HasMany
    {
        return $this->hasMany(StoreDomain::class);
    }

    /**
     * @return BelongsToMany<User, $this>
     */
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'store_users')
            ->using(StoreUser::class)
            ->withPivot('role');
    }

    /**
     * @return HasOne<StoreSettings, $this>
     */
    public function settings(): HasOne
    {
        return $this->hasOne(StoreSettings::class);
    }

    /**
     * @return HasMany<Theme, $this>
     */
    public function themes(): HasMany
    {
        return $this->hasMany(Theme::class);
    }

    /**
     * @return HasMany<Page, $this>
     */
    public function pages(): HasMany
    {
        return $this->hasMany(Page::class);
    }

    /**
     * @return HasMany<NavigationMenu, $this>
     */
    public function navigationMenus(): HasMany
    {
        return $this->hasMany(NavigationMenu::class);
    }

    /**
     * @return HasMany<Cart, $this>
     */
    public function carts(): HasMany
    {
        return $this->hasMany(Cart::class);
    }

    /**
     * @return HasMany<Checkout, $this>
     */
    public function checkouts(): HasMany
    {
        return $this->hasMany(Checkout::class);
    }

    /**
     * @return HasMany<ShippingZone, $this>
     */
    public function shippingZones(): HasMany
    {
        return $this->hasMany(ShippingZone::class);
    }

    /**
     * @return HasOne<TaxSettings, $this>
     */
    public function taxSettings(): HasOne
    {
        return $this->hasOne(TaxSettings::class);
    }

    /**
     * @return HasMany<Discount, $this>
     */
    public function discounts(): HasMany
    {
        return $this->hasMany(Discount::class);
    }

    /**
     * @return HasMany<Order, $this>
     */
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }

    /**
     * @return HasMany<AppInstallation, $this>
     */
    public function appInstallations(): HasMany
    {
        return $this->hasMany(AppInstallation::class);
    }

    /**
     * @return HasMany<WebhookSubscription, $this>
     */
    public function webhookSubscriptions(): HasMany
    {
        return $this->hasMany(WebhookSubscription::class);
    }

    public function isSuspended(): bool
    {
        return $this->status === StoreStatus::Suspended;
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'status' => StoreStatus::class,
        ];
    }
}


message: I’m going to patch the service and components now. The key point is that token lookup and customer lookup must both include `store_id`, and responses must stay generic so the flow does not reveal whether an email exists.

phase: commentary


type: message

role: assistant

text: I’m going to patch the service and components now. The key point is that token lookup and customer lookup must both include `store_id`, and responses must stay generic so the flow does not reveal whether an email exists.


status: completed

input:
*** Begin Patch
*** Update File: /Users/fabianwesner/Herd/shop/app/Services/CustomerPasswordResetService.php
@@
 <?php

 namespace App\Services;

+use App\Models\Customer;
+use App\Models\Store;
+use App\Notifications\CustomerResetPassword;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\RateLimiter;
+use Illuminate\Support\Str;
+
 class CustomerPasswordResetService
 {
-    /**
-     * Create a new class instance.
-     */
-    public function __construct()
+    public function sendResetLink(Store $store, string $email): void
     {
-        //
+        $email = $this->normalizeEmail($email);
+        $rateLimitKey = $this->rateLimitKey($store, $email);
+
+        if (RateLimiter::tooManyAttempts($rateLimitKey, 1)) {
+            return;
+        }
+
+        RateLimiter::hit($rateLimitKey, $this->throttleSeconds());
+
+        $customer = $this->findCustomer($store, $email);
+
+        if (! $customer instanceof Customer) {
+            return;
+        }
+
+        $token = Str::random(64);
+
+        DB::table($this->table())->updateOrInsert(
+            [
+                'store_id' => $store->getKey(),
+                'email' => $customer->email,
+            ],
+            [
+                'token' => Hash::make($token),
+                'created_at' => now(),
+            ],
+        );
+
+        $customer->notify(new CustomerResetPassword($token, $store));
+    }
+
+    public function reset(Store $store, string $email, string $token, string $password): bool
+    {
+        $email = $this->normalizeEmail($email);
+
+        $tokenRecord = DB::table($this->table())
+            ->where('store_id', $store->getKey())
+            ->whereRaw('lower(email) = ?', [$email])
+            ->first();
+
+        if (! $tokenRecord || $this->tokenExpired($tokenRecord->created_at) || ! Hash::check($token, $tokenRecord->token)) {
+            return false;
+        }
+
+        $customer = $this->findCustomer($store, $tokenRecord->email);
+
+        if (! $customer instanceof Customer) {
+            $this->deleteToken($store, $tokenRecord->email);
+
+            return false;
+        }
+
+        $customer->forceFill([
+            'password' => $password,
+        ])->save();
+
+        $this->deleteToken($store, $tokenRecord->email);
+
+        event(new \Illuminate\Auth\Events\PasswordReset($customer));
+
+        return true;
+    }
+
+    private function findCustomer(Store $store, string $email): ?Customer
+    {
+        return Customer::withoutGlobalScopes()
+            ->where('store_id', $store->getKey())
+            ->whereRaw('lower(email) = ?', [$this->normalizeEmail($email)])
+            ->first();
+    }
+
+    private function deleteToken(Store $store, string $email): void
+    {
+        DB::table($this->table())
+            ->where('store_id', $store->getKey())
+            ->whereRaw('lower(email) = ?', [$this->normalizeEmail($email)])
+            ->delete();
+    }
+
+    private function normalizeEmail(string $email): string
+    {
+        return Str::lower(trim($email));
+    }
+
+    private function tokenExpired(?string $createdAt): bool
+    {
+        if (! $createdAt) {
+            return true;
+        }
+
+        return now()->parse($createdAt)->addMinutes($this->expireMinutes())->isPast();
+    }
+
+    private function rateLimitKey(Store $store, string $email): string
+    {
+        return 'customer-password-reset:'.$store->getKey().':'.sha1($email);
+    }
+
+    private function table(): string
+    {
+        return config('auth.passwords.customers.table', 'customer_password_reset_tokens');
+    }
+
+    private function expireMinutes(): int
+    {
+        return (int) config('auth.passwords.customers.expire', 60);
+    }
+
+    private function throttleSeconds(): int
+    {
+        return (int) config('auth.passwords.customers.throttle', 60);
     }
 }
*** Update File: /Users/fabianwesner/Herd/shop/app/Notifications/CustomerResetPassword.php
@@
 namespace App\Notifications;

+use App\Models\Store;
 use Illuminate\Bus\Queueable;
-use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Notifications\Messages\MailMessage;
 use Illuminate\Notifications\Notification;

 class CustomerResetPassword extends Notification
 {
     use Queueable;

-    /**
-     * Create a new notification instance.
-     */
-    public function __construct()
-    {
-        //
+    public function __construct(
+        public string $token,
+        public Store $store,
+    ) {
     }
@@
     public function toMail(object $notifiable): MailMessage
     {
+        $resetUrl = route('account.password.reset', [
+            'token' => $this->token,
+            'email' => $notifiable->email,
+        ]);
+
         return (new MailMessage)
-            ->line('The introduction to the notification.')
-            ->action('Notification Action', url('/'))
-            ->line('Thank you for using our application!');
+            ->subject(__('Reset your :store password', ['store' => $this->store->name]))
+            ->line(__('You are receiving this email because we received a password reset request for your account.'))
+            ->action(__('Reset password'), $resetUrl)
+            ->line(__('This password reset link will expire in :count minutes.', [
+                'count' => config('auth.passwords.customers.expire', 60),
+            ]))
+            ->line(__('If you did not request a password reset, no further action is required.'));
     }
*** Update File: /Users/fabianwesner/Herd/shop/app/Livewire/Storefront/Account/Auth/ForgotPassword.php
@@
 namespace App\Livewire\Storefront\Account\Auth;

+use App\Models\Store;
+use App\Services\CustomerPasswordResetService;
+use Livewire\Attributes\Locked;
 use Livewire\Component;

 class ForgotPassword extends Component
 {
-    public function render()
+    #[Locked]
+    public int $storeId;
+
+    public string $email = '';
+
+    public function mount(): void
+    {
+        $store = app('current_store');
+
+        abort_unless($store instanceof Store, 404);
+
+        $this->storeId = $store->getKey();
+    }
+
+    public function send(CustomerPasswordResetService $passwords): void
+    {
+        $validated = $this->validate([
+            'email' => ['required', 'email', 'max:255'],
+        ]);
+
+        $passwords->sendResetLink(
+            Store::query()->findOrFail($this->storeId),
+            $validated['email'],
+        );
+
+        session()->flash('status', __('If an account matches that email, a reset link has been sent.'));
+    }
+
+    public function render(): mixed
     {
-        return view('livewire.storefront.account.auth.forgot-password');
+        return view('livewire.storefront.account.auth.forgot-password')
+            ->layout('layouts.auth');
     }
 }
*** Update File: /Users/fabianwesner/Herd/shop/app/Livewire/Storefront/Account/Auth/ResetPassword.php
@@
 namespace App\Livewire\Storefront\Account\Auth;

+use App\Models\Store;
+use App\Services\CustomerPasswordResetService;
+use Livewire\Attributes\Locked;
 use Livewire\Component;

 class ResetPassword extends Component
 {
-    public function render()
+    #[Locked]
+    public int $storeId;
+
+    #[Locked]
+    public string $token = '';
+
+    public string $email = '';
+
+    public string $password = '';
+
+    public string $password_confirmation = '';
+
+    public function mount(string $token): void
+    {
+        $store = app('current_store');
+
+        abort_unless($store instanceof Store, 404);
+
+        $this->storeId = $store->getKey();
+        $this->token = $token;
+        $this->email = (string) request('email', '');
+    }
+
+    public function resetPassword(CustomerPasswordResetService $passwords): void
+    {
+        $validated = $this->validate([
+            'email' => ['required', 'email', 'max:255'],
+            'password' => ['required', 'string', 'min:8', 'confirmed'],
+        ]);
+
+        $reset = $passwords->reset(
+            Store::query()->findOrFail($this->storeId),
+            $validated['email'],
+            $this->token,
+            $validated['password'],
+        );
+
+        if (! $reset) {
+            $this->addError('email', __('This password reset link is invalid or has expired.'));
+
+            return;
+        }
+
+        session()->flash('status', __('Your password has been reset. You may log in with your new password.'));
+
+        $this->redirectRoute('account.login', navigate: true);
+    }
+
+    public function render(): mixed
     {
-        return view('livewire.storefront.account.auth.reset-password');
+        return view('livewire.storefront.account.auth.reset-password')
+            ->layout('layouts.auth');
     }
 }
*** End Patch