diff --git a/app/Http/Controllers/Admin/PasskeysController.php b/app/Http/Controllers/Admin/PasskeysController.php index 61388175..e493926e 100644 --- a/app/Http/Controllers/Admin/PasskeysController.php +++ b/app/Http/Controllers/Admin/PasskeysController.php @@ -7,11 +7,37 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use App\Models\Passkey; use App\Models\User; +use Cose\Algorithm\Manager; +use Cose\Algorithm\Signature\ECDSA\ES256; +use Cose\Algorithm\Signature\EdDSA\Ed25519; +use Cose\Algorithm\Signature\RSA\RS256; +use Cose\Algorithms; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\View\View; +use ParagonIE\ConstantTime\Base64UrlSafe; +use Throwable; +use Webauthn\AttestationStatement\AttestationObjectLoader; +use Webauthn\AttestationStatement\AttestationStatementSupportManager; +use Webauthn\AttestationStatement\NoneAttestationStatementSupport; +use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler; +use Webauthn\AuthenticatorAssertionResponse; +use Webauthn\AuthenticatorAssertionResponseValidator; +use Webauthn\AuthenticatorAttestationResponse; +use Webauthn\AuthenticatorAttestationResponseValidator; +use Webauthn\AuthenticatorSelectionCriteria; +use Webauthn\Exception\WebauthnException; +use Webauthn\PublicKeyCredentialCreationOptions; +use Webauthn\PublicKeyCredentialLoader; +use Webauthn\PublicKeyCredentialParameters; +use Webauthn\PublicKeyCredentialRequestOptions; +use Webauthn\PublicKeyCredentialRpEntity; +use Webauthn\PublicKeyCredentialSource; +use Webauthn\PublicKeyCredentialUserEntity; /** * @psalm-suppress UnusedClass @@ -20,74 +46,214 @@ class PasskeysController extends Controller { public function index(): View { + /** @var User $user */ $user = auth()->user(); $passkeys = $user->passkey; return view('admin.passkeys.index', compact('passkeys')); } - public function save(Request $request): JsonResponse - { - $validator = Validator::make($request->all(), [ - 'id' => 'required|string|unique:App\Models\Passkey,passkey_id', - 'public_key' => 'required|file', - 'transports' => 'required|json', - 'challenge' => 'required|string', - ]); - - if ($validator->fails()) { - return response()->json([ - 'success' => false, - 'message' => 'Passkey could not be saved (validation failed)', - ]); - } - - $validated = $validator->validated(); - - if ( - !session()->has('challenge') || - $validated['challenge'] !== session('challenge') - ) { - return response()->json([ - 'success' => false, - 'message' => 'Passkey could not be saved (challenge failed)', - ]); - } - - $passkey = new Passkey(); - $passkey->passkey_id = $validated['id']; - $passkey->passkey = $validated['public_key']->get(); - $passkey->transports = json_decode($validated['transports'], true, 512, JSON_THROW_ON_ERROR); - $passkey->user_id = auth()->user()->id; - $passkey->save(); - - return response()->json([ - 'success' => true, - 'message' => 'Passkey saved successfully', - ]); - } - - public function init(): JsonResponse + public function getCreateOptions(): JsonResponse { /** @var User $user */ $user = auth()->user(); - $passkeys = $user->passkey()->get(); - $existing = $passkeys->map(function (Passkey $passkey) { - return [ - 'id' => $passkey->passkey_id, - 'transports' => $passkey->transports, - 'type' => 'public-key', - ]; - })->all(); + // RP Entity i.e. the application + $rpEntity = PublicKeyCredentialRpEntity::create( + config('app.name'), + config('url.longurl'), + ); - $challenge = Hash::make(random_bytes(32)); - session(['challenge' => $challenge]); + // User Entity + $userEntity = PublicKeyCredentialUserEntity::create( + $user->name, + (string) $user->id, + $user->name, + ); + + // Challenge + $challenge = random_bytes(16); + + // List of supported public key parameters + $pubKeyCredParams = collect([ + Algorithms::COSE_ALGORITHM_EDDSA, + Algorithms::COSE_ALGORITHM_ES256, + Algorithms::COSE_ALGORITHM_RS256, + ])->map( + fn ($algorithm) => PublicKeyCredentialParameters::create('public-key', $algorithm) + )->toArray(); + + $authenticatorSelectionCriteria = AuthenticatorSelectionCriteria::create( + userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED, + residentKey: AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_REQUIRED, + requireResidentKey: true, + ); + + $options = PublicKeyCredentialCreationOptions::create( + $rpEntity, + $userEntity, + $challenge, + $pubKeyCredParams, + authenticatorSelection: $authenticatorSelectionCriteria, + attestation: PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE + ); + + $options = json_encode($options, JSON_THROW_ON_ERROR); + + session(['create_options' => $options]); + + return JsonResponse::fromJsonString($options); + } + + public function create(Request $request): JsonResponse + { + /** @var User $user */ + $user = auth()->user(); + + $publicKeyCredentialCreationOptionsData = session('create_options'); + if (empty($publicKeyCredentialCreationOptionsData)) { + throw new WebAuthnException('No public key credential request options found'); + } + $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromString($publicKeyCredentialCreationOptionsData); + + // Unset session data to mitigate replay attacks + session()->forget('create_options'); + + $attestationSupportManager = AttestationStatementSupportManager::create(); + $attestationSupportManager->add(NoneAttestationStatementSupport::create()); + $attestationObjectLoader = AttestationObjectLoader::create($attestationSupportManager); + $publicKeyCredentialLoader = PublicKeyCredentialLoader::create($attestationObjectLoader); + + $publicKeyCredential = $publicKeyCredentialLoader->load(json_encode($request->all(), JSON_THROW_ON_ERROR)); + + if (!$publicKeyCredential->response instanceof AuthenticatorAttestationResponse) { + throw new WebAuthnException('Invalid response type'); + } + + $attestationStatementSupportManager = AttestationStatementSupportManager::create(); + $attestationStatementSupportManager->add(NoneAttestationStatementSupport::create()); + + $authenticatorAttestationResponseValidator = AuthenticatorAttestationResponseValidator::create( + attestationStatementSupportManager: $attestationStatementSupportManager, + publicKeyCredentialSourceRepository: null, + tokenBindingHandler: null, + extensionOutputCheckerHandler: ExtensionOutputCheckerHandler::create(), + ); + + $securedRelyingPartyId = []; + if (App::environment('local', 'development')) { + $securedRelyingPartyId = [config('url.longurl')]; + } + + $publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check( + authenticatorAttestationResponse: $publicKeyCredential->response, + publicKeyCredentialCreationOptions: $publicKeyCredentialCreationOptions, + request: config('url.longurl'), + securedRelyingPartyId: $securedRelyingPartyId, + ); + + $user->passkey()->create([ + 'passkey_id' => Base64UrlSafe::encodeUnpadded($publicKeyCredentialSource->publicKeyCredentialId), + 'passkey' => json_encode($publicKeyCredentialSource, JSON_THROW_ON_ERROR), + ]); return response()->json([ - 'challenge' => $challenge, - 'userId' => $user->name, - 'existing' => $existing, + 'success' => true, + 'message' => 'Passkey created successfully', + ]); + } + + public function getRequestOptions(): JsonResponse + { + $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create( + challenge: random_bytes(16), + userVerification: PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED + ); + + $publicKeyCredentialRequestOptions = json_encode($publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR); + + session(['request_options' => $publicKeyCredentialRequestOptions]); + + return JsonResponse::fromJsonString($publicKeyCredentialRequestOptions); + } + + public function login(Request $request): JsonResponse + { + $requestOptions = session('request_options'); + session()->forget('request_options'); + + if (empty($requestOptions)) { + return response()->json([ + 'success' => false, + 'message' => 'No request options found', + ], 400); + } + + $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::createFromString($requestOptions); + + $attestationSupportManager = AttestationStatementSupportManager::create(); + $attestationSupportManager->add(NoneAttestationStatementSupport::create()); + $attestationObjectLoader = AttestationObjectLoader::create($attestationSupportManager); + $publicKeyCredentialLoader = PublicKeyCredentialLoader::create($attestationObjectLoader); + + $publicKeyCredential = $publicKeyCredentialLoader->load(json_encode($request->all(), JSON_THROW_ON_ERROR)); + + if (!$publicKeyCredential->response instanceof AuthenticatorAssertionResponse) { + return response()->json([ + 'success' => false, + 'message' => 'Invalid response type', + ], 400); + } + + $passkey = Passkey::firstWhere('passkey_id', $publicKeyCredential->id); + if (!$passkey) { + return response()->json([ + 'success' => false, + 'message' => 'Passkey not found', + ], 404); + } + + $credential = PublicKeyCredentialSource::createFromArray(json_decode($passkey->passkey, true, 512, JSON_THROW_ON_ERROR)); + + $algorithmManager = Manager::create(); + $algorithmManager->add(new Ed25519()); + $algorithmManager->add(new ES256()); + $algorithmManager->add(new RS256()); + + $authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator( + publicKeyCredentialSourceRepository: null, + tokenBindingHandler: null, + extensionOutputCheckerHandler: ExtensionOutputCheckerHandler::create(), + algorithmManager: $algorithmManager, + ); + + $securedRelyingPartyId = []; + if (App::environment('local', 'development')) { + $securedRelyingPartyId = [config('url.longurl')]; + } + + try { + $authenticatorAssertionResponseValidator->check( + credentialId: $credential, + authenticatorAssertionResponse: $publicKeyCredential->response, + publicKeyCredentialRequestOptions: $publicKeyCredentialRequestOptions, + request: config('url.longurl'), + userHandle: null, + securedRelyingPartyId: $securedRelyingPartyId, + ); + } catch (Throwable) { + return response()->json([ + 'success' => false, + 'message' => 'Passkey could not be verified', + ], 500); + } + + $user = User::find($passkey->user_id); + Auth::login($user); + + return response()->json([ + 'success' => true, + 'message' => 'Passkey verified successfully', ]); } } diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index fd927644..27f34eab 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -34,7 +34,7 @@ class AuthController extends Controller $credentials = $request->only('name', 'password'); if (Auth::attempt($credentials, true)) { - return redirect()->intended('/'); + return redirect()->intended('/admin'); } return redirect()->route('login'); diff --git a/app/Models/Passkey.php b/app/Models/Passkey.php index 64461eca..343fa40d 100644 --- a/app/Models/Passkey.php +++ b/app/Models/Passkey.php @@ -13,27 +13,11 @@ class Passkey extends Model { use HasFactory; - /** - * Save and access the passkey appropriately. - */ - protected function passkey(): Attribute - { - return Attribute::make( - get: static fn ($value) => stream_get_contents($value), - set: static fn ($value) => pg_escape_bytea($value), - ); - } - - /** - * Save and access the transports appropriately. - */ - protected function transports(): Attribute - { - return Attribute::make( - get: static fn ($value) => json_decode($value, true, 512, JSON_THROW_ON_ERROR), - set: static fn ($value) => json_encode($value, JSON_THROW_ON_ERROR), - ); - } + /** @inerhitDoc */ + protected $fillable = [ + 'passkey_id', + 'passkey', + ]; public function user(): BelongsTo { diff --git a/composer.json b/composer.json index c41e6eff..20a38b42 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,8 @@ "mf2/mf2": "~0.3", "spatie/commonmark-highlighter": "^3.0", "spatie/laravel-ignition": "^2.1", - "symfony/html-sanitizer": "^6.1" + "symfony/html-sanitizer": "^6.1", + "web-auth/webauthn-lib": "^4.7" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.0", diff --git a/composer.lock b/composer.lock index 4b725649..0bf700cd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a0824739b9d145bf875bf9ae54e89b07", + "content-hash": "d870e46c1890e6dc609f0d1b65340ec4", "packages": [ { "name": "aws/aws-crt-php", @@ -2363,6 +2363,70 @@ }, "time": "2023-02-15T16:40:09+00:00" }, + { + "name": "lcobucci/clock", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "30a854ceb22bd87d83a7a4563b3f6312453945fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/30a854ceb22bd87d83a7a4563b3f6312453945fc", + "reference": "30a854ceb22bd87d83a7a4563b3f6312453945fc", + "shasum": "" + }, + "require": { + "php": "~8.2.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.26", + "lcobucci/coding-standard": "^10.0.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^10.0.17" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/3.1.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-03-20T19:12:25+00:00" + }, { "name": "lcobucci/jwt", "version": "5.0.0", @@ -3797,6 +3861,73 @@ }, "time": "2021-10-12T14:12:29+00:00" }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.1", @@ -5024,6 +5155,199 @@ ], "time": "2023-08-23T06:24:34+00:00" }, + { + "name": "spomky-labs/cbor-php", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/cbor-php.git", + "reference": "81d5dff7a1101d680729b5789f4359d01b15e6c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/81d5dff7a1101d680729b5789f4359d01b15e6c5", + "reference": "81d5dff7a1101d680729b5789f4359d01b15e6c5", + "shasum": "" + }, + "require": { + "brick/math": "^0.9|^0.10|^0.11", + "ext-mbstring": "*", + "php": ">=8.0" + }, + "require-dev": { + "ekino/phpstan-banned-code": "^1.0", + "ext-json": "*", + "infection/infection": "^0.26", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-beberlei-assert": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^10.0", + "qossmic/deptrac-shim": "^1.0", + "rector/rector": "^0.15", + "roave/security-advisories": "dev-latest", + "symfony/var-dumper": "^6.0", + "symplify/easy-coding-standard": "^11.1" + }, + "suggest": { + "ext-bcmath": "GMP or BCMath extensions will drastically improve the library performance. BCMath extension needed to handle the Big Float and Decimal Fraction Tags", + "ext-gmp": "GMP or BCMath extensions will drastically improve the library performance" + }, + "type": "library", + "autoload": { + "psr-4": { + "CBOR\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/Spomky-Labs/cbor-php/contributors" + } + ], + "description": "CBOR Encoder/Decoder for PHP", + "keywords": [ + "Concise Binary Object Representation", + "RFC7049", + "cbor" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/cbor-php/issues", + "source": "https://github.com/Spomky-Labs/cbor-php/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2023-02-28T21:37:12+00:00" + }, + { + "name": "spomky-labs/pki-framework", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/pki-framework.git", + "reference": "d3ba688bf40e7c6e0dabf065ee18fc210734e760" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/d3ba688bf40e7c6e0dabf065ee18fc210734e760", + "reference": "d3ba688bf40e7c6e0dabf065ee18fc210734e760", + "shasum": "" + }, + "require": { + "brick/math": "^0.10 || ^0.11", + "ext-mbstring": "*", + "php": ">=8.1" + }, + "require-dev": { + "ekino/phpstan-banned-code": "^1.0", + "ext-gmp": "*", + "ext-openssl": "*", + "infection/infection": "^0.26", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-beberlei-assert": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^10.0", + "rector/rector": "^0.15", + "roave/security-advisories": "dev-latest", + "symfony/phpunit-bridge": "^6.1", + "symfony/var-dumper": "^6.1", + "symplify/easy-coding-standard": "^11.1", + "thecodingmachine/phpstan-safe-rule": "^1.2" + }, + "suggest": { + "ext-bcmath": "For better performance (or GMP)", + "ext-gmp": "For better performance (or BCMath)", + "ext-openssl": "For OpenSSL based cyphering" + }, + "type": "library", + "autoload": { + "psr-4": { + "SpomkyLabs\\Pki\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joni Eskelinen", + "email": "jonieske@gmail.com", + "role": "Original developer" + }, + { + "name": "Florent Morselli", + "email": "florent.morselli@spomky-labs.com", + "role": "Spomky-Labs PKI Framework developer" + } + ], + "description": "A PHP framework for managing Public Key Infrastructures. It comprises X.509 public key certificates, attribute certificates, certification requests and certification path validation.", + "homepage": "https://github.com/spomky-labs/pki-framework", + "keywords": [ + "DER", + "Private Key", + "ac", + "algorithm identifier", + "asn.1", + "asn1", + "attribute certificate", + "certificate", + "certification request", + "cryptography", + "csr", + "decrypt", + "ec", + "encrypt", + "pem", + "pkcs", + "public key", + "rsa", + "sign", + "signature", + "verify", + "x.509", + "x.690", + "x509", + "x690" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/pki-framework/issues", + "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2023-02-13T17:21:24+00:00" + }, { "name": "symfony/console", "version": "v6.3.2", @@ -7552,6 +7876,258 @@ ], "time": "2022-03-08T17:03:00+00:00" }, + { + "name": "web-auth/cose-lib", + "version": "4.2.3", + "source": { + "type": "git", + "url": "https://github.com/web-auth/cose-lib.git", + "reference": "0ecad86d2d034ea22e2205d81c8cdec13d93a991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/0ecad86d2d034ea22e2205d81c8cdec13d93a991", + "reference": "0ecad86d2d034ea22e2205d81c8cdec13d93a991", + "shasum": "" + }, + "require": { + "brick/math": "^0.9|^0.10|^0.11", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "php": ">=8.1", + "spomky-labs/pki-framework": "^1.0" + }, + "require-dev": { + "ekino/phpstan-banned-code": "^1.0", + "infection/infection": "^0.27", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.7", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.2", + "phpunit/phpunit": "^10.1", + "qossmic/deptrac-shim": "^1.0", + "rector/rector": "^0.17", + "symfony/phpunit-bridge": "^6.1", + "symplify/easy-coding-standard": "^12.0" + }, + "suggest": { + "ext-bcmath": "For better performance, please install either GMP (recommended) or BCMath extension", + "ext-gmp": "For better performance, please install either GMP (recommended) or BCMath extension" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cose\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-auth/cose/contributors" + } + ], + "description": "CBOR Object Signing and Encryption (COSE) For PHP", + "homepage": "https://github.com/web-auth", + "keywords": [ + "COSE", + "RFC8152" + ], + "support": { + "issues": "https://github.com/web-auth/cose-lib/issues", + "source": "https://github.com/web-auth/cose-lib/tree/4.2.3" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2023-07-26T13:32:03+00:00" + }, + { + "name": "web-auth/metadata-service", + "version": "4.7.3", + "source": { + "type": "git", + "url": "https://github.com/web-auth/webauthn-metadata-service.git", + "reference": "1da1fc6d8055c75af4e46cde169d7b920b8af90a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-auth/webauthn-metadata-service/zipball/1da1fc6d8055c75af4e46cde169d7b920b8af90a", + "reference": "1da1fc6d8055c75af4e46cde169d7b920b8af90a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "lcobucci/clock": "^2.2|^3.0", + "paragonie/constant_time_encoding": "^2.6", + "php": ">=8.1", + "psr/clock": "^1.0", + "psr/event-dispatcher": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/log": "^1.0|^2.0|^3.0", + "spomky-labs/pki-framework": "^1.0", + "symfony/deprecation-contracts": "^3.2" + }, + "suggest": { + "psr/clock-implementation": "As of 4.5.x, the PSR Clock implementation will replace lcobucci/clock", + "psr/log-implementation": "Recommended to receive logs from the library", + "web-token/jwt-key-mgmt": "Mandatory for fetching Metadata Statement from distant sources", + "web-token/jwt-signature-algorithm-ecdsa": "Mandatory for fetching Metadata Statement from distant sources" + }, + "type": "library", + "extra": { + "thanks": { + "name": "web-auth/webauthn-framework", + "url": "https://github.com/web-auth/webauthn-framework" + } + }, + "autoload": { + "psr-4": { + "Webauthn\\MetadataService\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-auth/metadata-service/contributors" + } + ], + "description": "Metadata Service for FIDO2/Webauthn", + "homepage": "https://github.com/web-auth", + "keywords": [ + "FIDO2", + "fido", + "webauthn" + ], + "support": { + "source": "https://github.com/web-auth/webauthn-metadata-service/tree/4.7.3" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2023-10-07T13:59:48+00:00" + }, + { + "name": "web-auth/webauthn-lib", + "version": "4.7.3", + "source": { + "type": "git", + "url": "https://github.com/web-auth/webauthn-lib.git", + "reference": "d9b0d0563c561eaec5c24c46a551bf8ff23a030b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/d9b0d0563c561eaec5c24c46a551bf8ff23a030b", + "reference": "d9b0d0563c561eaec5c24c46a551bf8ff23a030b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "paragonie/constant_time_encoding": "^2.6", + "php": ">=8.1", + "psr/event-dispatcher": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/log": "^1.0|^2.0|^3.0", + "spomky-labs/cbor-php": "^3.0", + "symfony/uid": "^6.1", + "web-auth/cose-lib": "^4.2.3", + "web-auth/metadata-service": "self.version" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.1" + }, + "suggest": { + "psr/log-implementation": "Recommended to receive logs from the library", + "symfony/event-dispatcher": "Recommended to use dispatched events", + "web-token/jwt-key-mgmt": "Mandatory for the AndroidSafetyNet Attestation Statement support", + "web-token/jwt-signature-algorithm-ecdsa": "Recommended for the AndroidSafetyNet Attestation Statement support", + "web-token/jwt-signature-algorithm-eddsa": "Recommended for the AndroidSafetyNet Attestation Statement support", + "web-token/jwt-signature-algorithm-rsa": "Mandatory for the AndroidSafetyNet Attestation Statement support" + }, + "type": "library", + "extra": { + "thanks": { + "name": "web-auth/webauthn-framework", + "url": "https://github.com/web-auth/webauthn-framework" + } + }, + "autoload": { + "psr-4": { + "Webauthn\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-auth/webauthn-library/contributors" + } + ], + "description": "FIDO2/Webauthn Support For PHP", + "homepage": "https://github.com/web-auth", + "keywords": [ + "FIDO2", + "fido", + "webauthn" + ], + "support": { + "source": "https://github.com/web-auth/webauthn-lib/tree/4.7.3" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2023-10-15T11:54:31+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", @@ -12851,8 +13427,9 @@ "php": "^8.2", "ext-dom": "*", "ext-intl": "*", - "ext-json": "*" + "ext-json": "*", + "ext-pgsql": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/database/migrations/2023_08_27_113904_create_passkeys.php b/database/migrations/2023_08_27_113904_create_passkeys.php index a2450d65..5341ec9f 100644 --- a/database/migrations/2023_08_27_113904_create_passkeys.php +++ b/database/migrations/2023_08_27_113904_create_passkeys.php @@ -15,8 +15,7 @@ return new class extends Migration $table->id(); $table->unsignedBigInteger('user_id'); $table->string('passkey_id')->unique(); - $table->binary('passkey'); - $table->json('transports'); + $table->text('passkey'); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users'); diff --git a/public/assets/app.css b/public/assets/app.css index 71e197ea..b401b340 100644 --- a/public/assets/app.css +++ b/public/assets/app.css @@ -1 +1 @@ -:root{--font-family-headings:"Archer SSm A","Archer SSm B",serif;--font-family-body:"Verlag A","Verlag B",sans-serif;--font-family-monospace:"Operator Mono SSm A","Operator Mono SSm B",monospace;--font-size-sm:0.75rem;--font-size-base:1rem;--font-size-md:1.25rem;--font-size-lg:1.5rem;--font-size-xl:1.75rem;--font-size-xxl:2rem;--font-size-xxxl:2.25rem;--color-primary:#334700;--color-secondary:#e3ffb7;--color-link:#00649e;--color-link-visited:#bc7aff;--color-primary-shadow:rgba(16,25,0,.4)}@supports (color:color(display-p3 0 0 0)){:root{--color-primary:color(display-p3 0.21567 0.27838 0.03615);--color-secondary:color(display-p3 0.91016 0.99842 0.74082);--color-link:color(display-p3 0.01045 0.38351 0.63618);--color-link-visited:color(display-p3 0.70467 0.47549 0.99958);--color-primary-shadow:color(display-p3 0.06762 0.09646 0.00441/0.4)}}@supports (color:oklch(0% 0 0)){:root{--color-primary:oklch(36.8% 0.1 125.505);--color-secondary:oklch(96.3% 0.1 125.505);--color-link:oklch(48.09% 0.146 241.41);--color-link-visited:oklch(70.44% 0.21 304.41);--color-primary-shadow:oklch(19.56% 0.054 125.505/40%)}}body{background-color:var(--color-secondary);color:var(--color-primary);font-family:var(--font-family-body);font-size:var(--font-size-md)}code{font-family:var(--font-family-monospace)}h1,h2,h3,h4,h5,h6{font-family:var(--font-family-headings)}.grid{display:grid;grid-template-columns:5vw 1fr 5vw;grid-template-rows:-webkit-min-content 1fr -webkit-min-content;grid-template-rows:min-content 1fr min-content;row-gap:1rem}#site-header{grid-column:2/3;grid-row:1/2}main{grid-row:2/3}footer,main{grid-column:2/3}footer{grid-row:3/4}footer .iwc-logo{max-width:85vw}a{color:var(--color-link)}a:visited{color:var(--color-link-visited)}#site-header a:visited{color:var(--color-link)}.hljs{border-radius:.5rem}.p-bridgy-twitter-content{display:none}.h-card .hovercard{-webkit-box-orient:vertical;-webkit-box-direction:normal;background-color:var(--color-secondary);border-radius:1rem;-webkit-box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);display:none;-ms-flex-direction:column;flex-direction:column;gap:.5rem;opacity:0;padding:1rem;position:absolute;-webkit-transition:opacity .5s ease-in-out;transition:opacity .5s ease-in-out;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;z-index:100}.h-card .hovercard .u-photo{max-width:6rem}.h-card .hovercard .social-icon{height:1rem;width:1rem}.h-card:hover .hovercard{display:-webkit-box;display:-ms-flexbox;display:flex;opacity:1}.h-entry{-webkit-border-start:1px solid var(--color-primary);-webkit-padding-start:.5rem;border-inline-start:1px solid var(--color-primary);padding-inline-start:.5rem}.h-entry .reply-to{font-style:italic}.h-entry .post-info a{text-decoration:none}.h-entry .note-metadata{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;gap:1rem}.h-entry .note-metadata .syndication-links{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.h-entry .note-metadata .syndication-links a{text-decoration:none}.h-entry .note-metadata .syndication-links a svg{height:1rem;width:1rem} +:root{--font-family-headings:"Archer SSm A","Archer SSm B",serif;--font-family-body:"Verlag A","Verlag B",sans-serif;--font-family-monospace:"Operator Mono SSm A","Operator Mono SSm B",monospace;--font-size-sm:0.75rem;--font-size-base:1rem;--font-size-md:1.25rem;--font-size-lg:1.5rem;--font-size-xl:1.75rem;--font-size-xxl:2rem;--font-size-xxxl:2.25rem;--color-primary:#334700;--color-secondary:#e3ffb7;--color-link:#00649e;--color-link-visited:#bc7aff;--color-primary-shadow:rgba(16,25,0,.4)}@supports (color:color(display-p3 0 0 0)){:root{--color-primary:color(display-p3 0.21567 0.27838 0.03615);--color-secondary:color(display-p3 0.91016 0.99842 0.74082);--color-link:color(display-p3 0.01045 0.38351 0.63618);--color-link-visited:color(display-p3 0.70467 0.47549 0.99958);--color-primary-shadow:color(display-p3 0.06762 0.09646 0.00441/0.4)}}@supports (color:oklch(0% 0 0)){:root{--color-primary:oklch(36.8% 0.1 125.505deg);--color-secondary:oklch(96.3% 0.1 125.505deg);--color-link:oklch(48.09% 0.146 241.41deg);--color-link-visited:oklch(70.44% 0.21 304.41deg);--color-primary-shadow:oklch(19.56% 0.054 125.505deg/40%)}}body{background-color:var(--color-secondary);color:var(--color-primary);font-family:var(--font-family-body);font-size:var(--font-size-md)}code{font-family:var(--font-family-monospace)}h1,h2,h3,h4,h5,h6{font-family:var(--font-family-headings)}.grid{display:grid;grid-template-columns:5vw 1fr 5vw;grid-template-rows:-webkit-min-content 1fr -webkit-min-content;grid-template-rows:min-content 1fr min-content;row-gap:1rem}#site-header{grid-column:2/3;grid-row:1/2}main{grid-row:2/3}footer,main{grid-column:2/3}footer{grid-row:3/4}footer .iwc-logo{max-width:85vw}footer .footer-actions{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;gap:1rem}a{color:var(--color-link)}a:visited{color:var(--color-link-visited)}#site-header a:visited,a.auth:visited{color:var(--color-link)}.hljs{border-radius:.5rem}.h-card .hovercard{-webkit-box-orient:vertical;-webkit-box-direction:normal;background-color:var(--color-secondary);border-radius:1rem;-webkit-box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);display:none;-ms-flex-direction:column;flex-direction:column;gap:.5rem;opacity:0;padding:1rem;position:absolute;-webkit-transition:opacity .5s ease-in-out;transition:opacity .5s ease-in-out;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;z-index:100}.h-card .hovercard .u-photo{max-width:6rem}.h-card .hovercard .social-icon{height:1rem;width:1rem}.h-card:hover .hovercard{display:-webkit-box;display:-ms-flexbox;display:flex;opacity:1}.h-entry{-webkit-border-start:1px solid var(--color-primary);-webkit-padding-start:.5rem;border-inline-start:1px solid var(--color-primary);padding-inline-start:.5rem}.h-entry .reply-to{font-style:italic}.h-entry .post-info a{text-decoration:none}.h-entry .note-metadata{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;gap:1rem}.h-entry .note-metadata .syndication-links{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.h-entry .note-metadata .syndication-links a{text-decoration:none}.h-entry .note-metadata .syndication-links a svg{height:1rem;width:1rem} diff --git a/public/assets/app.css.br b/public/assets/app.css.br index f38bc392..26e19ed9 100644 Binary files a/public/assets/app.css.br and b/public/assets/app.css.br differ diff --git a/public/assets/app.js b/public/assets/app.js index f30dcde8..35542437 100644 --- a/public/assets/app.js +++ b/public/assets/app.js @@ -1,213 +1 @@ -/* - * ATTENTION: An "eval-source-map" devtool has been used. - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (function() { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./resources/js/app.js": -/*!*****************************!*\ - !*** ./resources/js/app.js ***! - \*****************************/ -/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _css_app_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../css/app.css */ \"./resources/css/app.css\");\n/* harmony import */ var _auth_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./auth.js */ \"./resources/js/auth.js\");\n\n\nlet auth = new _auth_js__WEBPACK_IMPORTED_MODULE_1__.Auth();\ndocument.querySelectorAll('.add-passkey').forEach(el => {\n el.addEventListener('click', () => {\n auth.register();\n });\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvdXJjZXMvanMvYXBwLmpzIiwibWFwcGluZ3MiOiI7OztBQUF3QjtBQUVTO0FBRWpDLElBQUlDLElBQUksR0FBRyxJQUFJRCwwQ0FBSSxDQUFDLENBQUM7QUFFckJFLFFBQVEsQ0FBQ0MsZ0JBQWdCLENBQUMsY0FBYyxDQUFDLENBQUNDLE9BQU8sQ0FBRUMsRUFBRSxJQUFLO0VBQ3hEQSxFQUFFLENBQUNDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxNQUFNO0lBQ2pDTCxJQUFJLENBQUNNLFFBQVEsQ0FBQyxDQUFDO0VBQ2pCLENBQUMsQ0FBQztBQUNKLENBQUMsQ0FBQyIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9yZXNvdXJjZXMvanMvYXBwLmpzPzZkNDAiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICcuLi9jc3MvYXBwLmNzcyc7XG5cbmltcG9ydCB7IEF1dGggfSBmcm9tICcuL2F1dGguanMnO1xuXG5sZXQgYXV0aCA9IG5ldyBBdXRoKCk7XG5cbmRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5hZGQtcGFzc2tleScpLmZvckVhY2goKGVsKSA9PiB7XG4gIGVsLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4ge1xuICAgIGF1dGgucmVnaXN0ZXIoKTtcbiAgfSk7XG59KTtcbiJdLCJuYW1lcyI6WyJBdXRoIiwiYXV0aCIsImRvY3VtZW50IiwicXVlcnlTZWxlY3RvckFsbCIsImZvckVhY2giLCJlbCIsImFkZEV2ZW50TGlzdGVuZXIiLCJyZWdpc3RlciJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resources/js/app.js\n"); - -/***/ }), - -/***/ "./resources/js/auth.js": -/*!******************************!*\ - !*** ./resources/js/auth.js ***! - \******************************/ -/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Auth: function() { return /* binding */ Auth; }\n/* harmony export */ });\nclass Auth {\n constructor() {}\n async register() {\n const {\n challenge,\n userId,\n existing\n } = await this.getRegisterData();\n const publicKeyCredentialCreationOptions = {\n challenge: new TextEncoder().encode(challenge),\n rp: {\n name: 'JB'\n },\n user: {\n id: new TextEncoder().encode(userId),\n name: 'jonny@jonnybarnes.uk',\n displayName: 'Jonny'\n },\n pubKeyCredParams: [{\n alg: -8,\n type: 'public-key'\n },\n // Ed25519\n {\n alg: -7,\n type: 'public-key'\n },\n // ES256\n {\n alg: -257,\n type: 'public-key'\n } // RS256\n ],\n\n excludeCredentials: existing,\n authenticatorSelection: {\n userVerification: 'preferred',\n residentKey: 'required'\n },\n timeout: 60000\n };\n const publicKeyCredential = await navigator.credentials.create({\n publicKey: publicKeyCredentialCreationOptions\n });\n if (!publicKeyCredential) {\n throw new Error('Error generating a passkey');\n }\n const {\n id // the key id a.k.a. kid\n } = publicKeyCredential;\n const publicKey = publicKeyCredential.response.getPublicKey();\n const transports = publicKeyCredential.response.getTransports();\n const response = publicKeyCredential.response;\n const clientJSONArrayBuffer = response.clientDataJSON;\n const clientJSON = JSON.parse(new TextDecoder().decode(clientJSONArrayBuffer));\n const clientChallenge = clientJSON.challenge;\n // base64 decode the challenge\n const clientChallengeDecoded = atob(clientChallenge);\n const saved = await this.savePasskey(id, publicKey, transports, clientChallengeDecoded);\n if (saved) {\n window.location.reload();\n } else {\n alert('There was an error saving the passkey');\n }\n }\n async getRegisterData() {\n const response = await fetch('/admin/passkeys/init');\n return await response.json();\n }\n async savePasskey(id, publicKey, transports, challenge) {\n const formData = new FormData();\n formData.append('id', id);\n formData.append('transports', JSON.stringify(transports));\n formData.append('challenge', challenge);\n\n // Convert the ArrayBuffer to a Uint8Array\n const publicKeyArray = new Uint8Array(publicKey);\n\n // Create a Blob from the Uint8Array\n const publicKeyBlob = new Blob([publicKeyArray], {\n type: 'application/octet-stream'\n });\n formData.append('public_key', publicKeyBlob);\n const response = await fetch('/admin/passkeys/save', {\n method: 'POST',\n body: formData,\n headers: {\n 'X-CSRF-TOKEN': document.querySelector('meta[name=\"csrf-token\"]').getAttribute('content')\n }\n });\n return response.ok;\n }\n}\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvdXJjZXMvanMvYXV0aC5qcyIsIm1hcHBpbmdzIjoiOzs7O0FBQUEsTUFBTUEsSUFBSSxDQUFDO0VBQ1RDLFdBQVdBLENBQUEsRUFBRyxDQUFDO0VBRWYsTUFBTUMsUUFBUUEsQ0FBQSxFQUFHO0lBQ2YsTUFBTTtNQUFFQyxTQUFTO01BQUVDLE1BQU07TUFBRUM7SUFBUyxDQUFDLEdBQUcsTUFBTSxJQUFJLENBQUNDLGVBQWUsQ0FBQyxDQUFDO0lBRXBFLE1BQU1DLGtDQUFrQyxHQUFHO01BQ3pDSixTQUFTLEVBQUUsSUFBSUssV0FBVyxDQUFDLENBQUMsQ0FBQ0MsTUFBTSxDQUFDTixTQUFTLENBQUM7TUFDOUNPLEVBQUUsRUFBRTtRQUNGQyxJQUFJLEVBQUU7TUFDUixDQUFDO01BQ0RDLElBQUksRUFBRTtRQUNKQyxFQUFFLEVBQUUsSUFBSUwsV0FBVyxDQUFDLENBQUMsQ0FBQ0MsTUFBTSxDQUFDTCxNQUFNLENBQUM7UUFDcENPLElBQUksRUFBRSxzQkFBc0I7UUFDNUJHLFdBQVcsRUFBRTtNQUNmLENBQUM7TUFDREMsZ0JBQWdCLEVBQUUsQ0FDaEI7UUFBQ0MsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUFFQyxJQUFJLEVBQUU7TUFBWSxDQUFDO01BQUU7TUFDL0I7UUFBQ0QsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUFFQyxJQUFJLEVBQUU7TUFBWSxDQUFDO01BQUU7TUFDL0I7UUFBQ0QsR0FBRyxFQUFFLENBQUMsR0FBRztRQUFFQyxJQUFJLEVBQUU7TUFBWSxDQUFDLENBQUU7TUFBQSxDQUNsQzs7TUFDREMsa0JBQWtCLEVBQUViLFFBQVE7TUFDNUJjLHNCQUFzQixFQUFFO1FBQ3RCQyxnQkFBZ0IsRUFBRSxXQUFXO1FBQzdCQyxXQUFXLEVBQUU7TUFDZixDQUFDO01BQ0RDLE9BQU8sRUFBRTtJQUNYLENBQUM7SUFFRCxNQUFNQyxtQkFBbUIsR0FBRyxNQUFNQyxTQUFTLENBQUNDLFdBQVcsQ0FBQ0MsTUFBTSxDQUFDO01BQzdEQyxTQUFTLEVBQUVwQjtJQUNiLENBQUMsQ0FBQztJQUNGLElBQUksQ0FBQ2dCLG1CQUFtQixFQUFFO01BQ3hCLE1BQU0sSUFBSUssS0FBSyxDQUFDLDRCQUE0QixDQUFDO0lBQy9DO0lBQ0EsTUFBTTtNQUNKZixFQUFFLENBQUM7SUFDTCxDQUFDLEdBQUdVLG1CQUFtQjtJQUN2QixNQUFNSSxTQUFTLEdBQUdKLG1CQUFtQixDQUFDTSxRQUFRLENBQUNDLFlBQVksQ0FBQyxDQUFDO0lBQzdELE1BQU1DLFVBQVUsR0FBR1IsbUJBQW1CLENBQUNNLFFBQVEsQ0FBQ0csYUFBYSxDQUFDLENBQUM7SUFDL0QsTUFBTUgsUUFBUSxHQUFHTixtQkFBbUIsQ0FBQ00sUUFBUTtJQUM3QyxNQUFNSSxxQkFBcUIsR0FBR0osUUFBUSxDQUFDSyxjQUFjO0lBQ3JELE1BQU1DLFVBQVUsR0FBR0MsSUFBSSxDQUFDQyxLQUFLLENBQUMsSUFBSUMsV0FBVyxDQUFDLENBQUMsQ0FBQ0MsTUFBTSxDQUFDTixxQkFBcUIsQ0FBQyxDQUFDO0lBQzlFLE1BQU1PLGVBQWUsR0FBR0wsVUFBVSxDQUFDaEMsU0FBUztJQUM1QztJQUNBLE1BQU1zQyxzQkFBc0IsR0FBR0MsSUFBSSxDQUFDRixlQUFlLENBQUM7SUFFcEQsTUFBTUcsS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDQyxXQUFXLENBQUMvQixFQUFFLEVBQUVjLFNBQVMsRUFBRUksVUFBVSxFQUFFVSxzQkFBc0IsQ0FBQztJQUV2RixJQUFJRSxLQUFLLEVBQUU7TUFDVEUsTUFBTSxDQUFDQyxRQUFRLENBQUNDLE1BQU0sQ0FBQyxDQUFDO0lBQzFCLENBQUMsTUFBTTtNQUNMQyxLQUFLLENBQUMsdUNBQXVDLENBQUM7SUFDaEQ7RUFDRjtFQUVBLE1BQU0xQyxlQUFlQSxDQUFBLEVBQUc7SUFDdEIsTUFBTXVCLFFBQVEsR0FBRyxNQUFNb0IsS0FBSyxDQUFDLHNCQUFzQixDQUFDO0lBRXBELE9BQU8sTUFBTXBCLFFBQVEsQ0FBQ3FCLElBQUksQ0FBQyxDQUFDO0VBQzlCO0VBRUEsTUFBTU4sV0FBV0EsQ0FBQy9CLEVBQUUsRUFBRWMsU0FBUyxFQUFFSSxVQUFVLEVBQUU1QixTQUFTLEVBQUU7SUFDdEQsTUFBTWdELFFBQVEsR0FBRyxJQUFJQyxRQUFRLENBQUMsQ0FBQztJQUMvQkQsUUFBUSxDQUFDRSxNQUFNLENBQUMsSUFBSSxFQUFFeEMsRUFBRSxDQUFDO0lBQ3pCc0MsUUFBUSxDQUFDRSxNQUFNLENBQUMsWUFBWSxFQUFFakIsSUFBSSxDQUFDa0IsU0FBUyxDQUFDdkIsVUFBVSxDQUFDLENBQUM7SUFDekRvQixRQUFRLENBQUNFLE1BQU0sQ0FBQyxXQUFXLEVBQUVsRCxTQUFTLENBQUM7O0lBRXZDO0lBQ0EsTUFBTW9ELGNBQWMsR0FBRyxJQUFJQyxVQUFVLENBQUM3QixTQUFTLENBQUM7O0lBRWhEO0lBQ0EsTUFBTThCLGFBQWEsR0FBRyxJQUFJQyxJQUFJLENBQUMsQ0FBQ0gsY0FBYyxDQUFDLEVBQUU7TUFBRXRDLElBQUksRUFBRTtJQUEyQixDQUFDLENBQUM7SUFFdEZrQyxRQUFRLENBQUNFLE1BQU0sQ0FBQyxZQUFZLEVBQUVJLGFBQWEsQ0FBQztJQUU1QyxNQUFNNUIsUUFBUSxHQUFHLE1BQU1vQixLQUFLLENBQUMsc0JBQXNCLEVBQUU7TUFDbkRVLE1BQU0sRUFBRSxNQUFNO01BQ2RDLElBQUksRUFBRVQsUUFBUTtNQUNkVSxPQUFPLEVBQUU7UUFDUCxjQUFjLEVBQUVDLFFBQVEsQ0FBQ0MsYUFBYSxDQUFDLHlCQUF5QixDQUFDLENBQUNDLFlBQVksQ0FBQyxTQUFTO01BQzFGO0lBQ0YsQ0FBQyxDQUFDO0lBRUYsT0FBT25DLFFBQVEsQ0FBQ29DLEVBQUU7RUFDcEI7QUFDRiIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9yZXNvdXJjZXMvanMvYXV0aC5qcz84ZjRmIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEF1dGgge1xuICBjb25zdHJ1Y3RvcigpIHt9XG5cbiAgYXN5bmMgcmVnaXN0ZXIoKSB7XG4gICAgY29uc3QgeyBjaGFsbGVuZ2UsIHVzZXJJZCwgZXhpc3RpbmcgfSA9IGF3YWl0IHRoaXMuZ2V0UmVnaXN0ZXJEYXRhKCk7XG5cbiAgICBjb25zdCBwdWJsaWNLZXlDcmVkZW50aWFsQ3JlYXRpb25PcHRpb25zID0ge1xuICAgICAgY2hhbGxlbmdlOiBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUoY2hhbGxlbmdlKSxcbiAgICAgIHJwOiB7XG4gICAgICAgIG5hbWU6ICdKQicsXG4gICAgICB9LFxuICAgICAgdXNlcjoge1xuICAgICAgICBpZDogbmV3IFRleHRFbmNvZGVyKCkuZW5jb2RlKHVzZXJJZCksXG4gICAgICAgIG5hbWU6ICdqb25ueUBqb25ueWJhcm5lcy51aycsXG4gICAgICAgIGRpc3BsYXlOYW1lOiAnSm9ubnknLFxuICAgICAgfSxcbiAgICAgIHB1YktleUNyZWRQYXJhbXM6IFtcbiAgICAgICAge2FsZzogLTgsIHR5cGU6ICdwdWJsaWMta2V5J30sIC8vIEVkMjU1MTlcbiAgICAgICAge2FsZzogLTcsIHR5cGU6ICdwdWJsaWMta2V5J30sIC8vIEVTMjU2XG4gICAgICAgIHthbGc6IC0yNTcsIHR5cGU6ICdwdWJsaWMta2V5J30sIC8vIFJTMjU2XG4gICAgICBdLFxuICAgICAgZXhjbHVkZUNyZWRlbnRpYWxzOiBleGlzdGluZyxcbiAgICAgIGF1dGhlbnRpY2F0b3JTZWxlY3Rpb246IHtcbiAgICAgICAgdXNlclZlcmlmaWNhdGlvbjogJ3ByZWZlcnJlZCcsXG4gICAgICAgIHJlc2lkZW50S2V5OiAncmVxdWlyZWQnLFxuICAgICAgfSxcbiAgICAgIHRpbWVvdXQ6IDYwMDAwLFxuICAgIH07XG5cbiAgICBjb25zdCBwdWJsaWNLZXlDcmVkZW50aWFsID0gYXdhaXQgbmF2aWdhdG9yLmNyZWRlbnRpYWxzLmNyZWF0ZSh7XG4gICAgICBwdWJsaWNLZXk6IHB1YmxpY0tleUNyZWRlbnRpYWxDcmVhdGlvbk9wdGlvbnNcbiAgICB9KTtcbiAgICBpZiAoIXB1YmxpY0tleUNyZWRlbnRpYWwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRXJyb3IgZ2VuZXJhdGluZyBhIHBhc3NrZXknKTtcbiAgICB9XG4gICAgY29uc3Qge1xuICAgICAgaWQgLy8gdGhlIGtleSBpZCBhLmsuYS4ga2lkXG4gICAgfSA9IHB1YmxpY0tleUNyZWRlbnRpYWw7XG4gICAgY29uc3QgcHVibGljS2V5ID0gcHVibGljS2V5Q3JlZGVudGlhbC5yZXNwb25zZS5nZXRQdWJsaWNLZXkoKTtcbiAgICBjb25zdCB0cmFuc3BvcnRzID0gcHVibGljS2V5Q3JlZGVudGlhbC5yZXNwb25zZS5nZXRUcmFuc3BvcnRzKCk7XG4gICAgY29uc3QgcmVzcG9uc2UgPSBwdWJsaWNLZXlDcmVkZW50aWFsLnJlc3BvbnNlO1xuICAgIGNvbnN0IGNsaWVudEpTT05BcnJheUJ1ZmZlciA9IHJlc3BvbnNlLmNsaWVudERhdGFKU09OO1xuICAgIGNvbnN0IGNsaWVudEpTT04gPSBKU09OLnBhcnNlKG5ldyBUZXh0RGVjb2RlcigpLmRlY29kZShjbGllbnRKU09OQXJyYXlCdWZmZXIpKTtcbiAgICBjb25zdCBjbGllbnRDaGFsbGVuZ2UgPSBjbGllbnRKU09OLmNoYWxsZW5nZTtcbiAgICAvLyBiYXNlNjQgZGVjb2RlIHRoZSBjaGFsbGVuZ2VcbiAgICBjb25zdCBjbGllbnRDaGFsbGVuZ2VEZWNvZGVkID0gYXRvYihjbGllbnRDaGFsbGVuZ2UpO1xuXG4gICAgY29uc3Qgc2F2ZWQgPSBhd2FpdCB0aGlzLnNhdmVQYXNza2V5KGlkLCBwdWJsaWNLZXksIHRyYW5zcG9ydHMsIGNsaWVudENoYWxsZW5nZURlY29kZWQpO1xuXG4gICAgaWYgKHNhdmVkKSB7XG4gICAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGFsZXJ0KCdUaGVyZSB3YXMgYW4gZXJyb3Igc2F2aW5nIHRoZSBwYXNza2V5Jyk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZ2V0UmVnaXN0ZXJEYXRhKCkge1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goJy9hZG1pbi9wYXNza2V5cy9pbml0Jyk7XG5cbiAgICByZXR1cm4gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICB9XG5cbiAgYXN5bmMgc2F2ZVBhc3NrZXkoaWQsIHB1YmxpY0tleSwgdHJhbnNwb3J0cywgY2hhbGxlbmdlKSB7XG4gICAgY29uc3QgZm9ybURhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAgICBmb3JtRGF0YS5hcHBlbmQoJ2lkJywgaWQpO1xuICAgIGZvcm1EYXRhLmFwcGVuZCgndHJhbnNwb3J0cycsIEpTT04uc3RyaW5naWZ5KHRyYW5zcG9ydHMpKTtcbiAgICBmb3JtRGF0YS5hcHBlbmQoJ2NoYWxsZW5nZScsIGNoYWxsZW5nZSk7XG5cbiAgICAvLyBDb252ZXJ0IHRoZSBBcnJheUJ1ZmZlciB0byBhIFVpbnQ4QXJyYXlcbiAgICBjb25zdCBwdWJsaWNLZXlBcnJheSA9IG5ldyBVaW50OEFycmF5KHB1YmxpY0tleSk7XG5cbiAgICAvLyBDcmVhdGUgYSBCbG9iIGZyb20gdGhlIFVpbnQ4QXJyYXlcbiAgICBjb25zdCBwdWJsaWNLZXlCbG9iID0gbmV3IEJsb2IoW3B1YmxpY0tleUFycmF5XSwgeyB0eXBlOiAnYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtJyB9KTtcblxuICAgIGZvcm1EYXRhLmFwcGVuZCgncHVibGljX2tleScsIHB1YmxpY0tleUJsb2IpO1xuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCgnL2FkbWluL3Bhc3NrZXlzL3NhdmUnLCB7XG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGJvZHk6IGZvcm1EYXRhLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAnWC1DU1JGLVRPS0VOJzogZG9jdW1lbnQucXVlcnlTZWxlY3RvcignbWV0YVtuYW1lPVwiY3NyZi10b2tlblwiXScpLmdldEF0dHJpYnV0ZSgnY29udGVudCcpLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIHJldHVybiByZXNwb25zZS5vaztcbiAgfVxufVxuXG5leHBvcnQgeyBBdXRoIH07XG4iXSwibmFtZXMiOlsiQXV0aCIsImNvbnN0cnVjdG9yIiwicmVnaXN0ZXIiLCJjaGFsbGVuZ2UiLCJ1c2VySWQiLCJleGlzdGluZyIsImdldFJlZ2lzdGVyRGF0YSIsInB1YmxpY0tleUNyZWRlbnRpYWxDcmVhdGlvbk9wdGlvbnMiLCJUZXh0RW5jb2RlciIsImVuY29kZSIsInJwIiwibmFtZSIsInVzZXIiLCJpZCIsImRpc3BsYXlOYW1lIiwicHViS2V5Q3JlZFBhcmFtcyIsImFsZyIsInR5cGUiLCJleGNsdWRlQ3JlZGVudGlhbHMiLCJhdXRoZW50aWNhdG9yU2VsZWN0aW9uIiwidXNlclZlcmlmaWNhdGlvbiIsInJlc2lkZW50S2V5IiwidGltZW91dCIsInB1YmxpY0tleUNyZWRlbnRpYWwiLCJuYXZpZ2F0b3IiLCJjcmVkZW50aWFscyIsImNyZWF0ZSIsInB1YmxpY0tleSIsIkVycm9yIiwicmVzcG9uc2UiLCJnZXRQdWJsaWNLZXkiLCJ0cmFuc3BvcnRzIiwiZ2V0VHJhbnNwb3J0cyIsImNsaWVudEpTT05BcnJheUJ1ZmZlciIsImNsaWVudERhdGFKU09OIiwiY2xpZW50SlNPTiIsIkpTT04iLCJwYXJzZSIsIlRleHREZWNvZGVyIiwiZGVjb2RlIiwiY2xpZW50Q2hhbGxlbmdlIiwiY2xpZW50Q2hhbGxlbmdlRGVjb2RlZCIsImF0b2IiLCJzYXZlZCIsInNhdmVQYXNza2V5Iiwid2luZG93IiwibG9jYXRpb24iLCJyZWxvYWQiLCJhbGVydCIsImZldGNoIiwianNvbiIsImZvcm1EYXRhIiwiRm9ybURhdGEiLCJhcHBlbmQiLCJzdHJpbmdpZnkiLCJwdWJsaWNLZXlBcnJheSIsIlVpbnQ4QXJyYXkiLCJwdWJsaWNLZXlCbG9iIiwiQmxvYiIsIm1ldGhvZCIsImJvZHkiLCJoZWFkZXJzIiwiZG9jdW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiZ2V0QXR0cmlidXRlIiwib2siXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./resources/js/auth.js\n"); - -/***/ }), - -/***/ "./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./resources/css/app.css": -/*!***********************************************************************************************************************************************************************!*\ - !*** ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./resources/css/app.css ***! - \***********************************************************************************************************************************************************************/ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/sourceMaps.js */ \"./node_modules/css-loader/dist/runtime/sourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \":root{--font-family-headings:\\\"Archer SSm A\\\",\\\"Archer SSm B\\\",serif;--font-family-body:\\\"Verlag A\\\",\\\"Verlag B\\\",sans-serif;--font-family-monospace:\\\"Operator Mono SSm A\\\",\\\"Operator Mono SSm B\\\",monospace;--font-size-sm:0.75rem;--font-size-base:1rem;--font-size-md:1.25rem;--font-size-lg:1.5rem;--font-size-xl:1.75rem;--font-size-xxl:2rem;--font-size-xxxl:2.25rem;--color-primary:#334700;--color-secondary:#e3ffb7;--color-link:#00649e;--color-link-visited:#bc7aff;--color-primary-shadow:rgba(16,25,0,.4)}@supports (color:color(display-p3 0 0 0)){:root{--color-primary:color(display-p3 0.21567 0.27838 0.03615);--color-secondary:color(display-p3 0.91016 0.99842 0.74082);--color-link:color(display-p3 0.01045 0.38351 0.63618);--color-link-visited:color(display-p3 0.70467 0.47549 0.99958);--color-primary-shadow:color(display-p3 0.06762 0.09646 0.00441/0.4)}}@supports (color:oklch(0% 0 0)){:root{--color-primary:oklch(36.8% 0.1 125.505deg);--color-secondary:oklch(96.3% 0.1 125.505deg);--color-link:oklch(48.09% 0.146 241.41deg);--color-link-visited:oklch(70.44% 0.21 304.41deg);--color-primary-shadow:oklch(19.56% 0.054 125.505deg/40%)}}body{background-color:var(--color-secondary);color:var(--color-primary);font-family:var(--font-family-body);font-size:var(--font-size-md)}code{font-family:var(--font-family-monospace)}h1,h2,h3,h4,h5,h6{font-family:var(--font-family-headings)}.grid{display:grid;grid-template-columns:5vw 1fr 5vw;grid-template-rows:-webkit-min-content 1fr -webkit-min-content;grid-template-rows:min-content 1fr min-content;row-gap:1rem}#site-header{grid-column:2/3;grid-row:1/2}main{grid-row:2/3}footer,main{grid-column:2/3}footer{grid-row:3/4}footer .iwc-logo{max-width:85vw}a{color:var(--color-link)}a:visited{color:var(--color-link-visited)}#site-header a:visited{color:var(--color-link)}.hljs{border-radius:.5rem}.h-card .hovercard{-webkit-box-orient:vertical;-webkit-box-direction:normal;background-color:var(--color-secondary);border-radius:1rem;-webkit-box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);box-shadow:0 .5rem .5rem .5rem var(--color-primary-shadow);display:none;-ms-flex-direction:column;flex-direction:column;gap:.5rem;opacity:0;padding:1rem;position:absolute;-webkit-transition:opacity .5s ease-in-out;transition:opacity .5s ease-in-out;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;z-index:100}.h-card .hovercard .u-photo{max-width:6rem}.h-card .hovercard .social-icon{height:1rem;width:1rem}.h-card:hover .hovercard{display:-webkit-box;display:-ms-flexbox;display:flex;opacity:1}.h-entry{-webkit-border-start:1px solid var(--color-primary);-webkit-padding-start:.5rem;border-inline-start:1px solid var(--color-primary);padding-inline-start:.5rem}.h-entry .reply-to{font-style:italic}.h-entry .post-info a{text-decoration:none}.h-entry .note-metadata{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;gap:1rem}.h-entry .note-metadata .syndication-links{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.h-entry .note-metadata .syndication-links a{text-decoration:none}.h-entry .note-metadata .syndication-links a svg{height:1rem;width:1rem}\", \"\",{\"version\":3,\"sources\":[\"webpack://./resources/css/variables.css\",\"webpack://./resources/css/fonts.css\",\"webpack://./resources/css/colours.css\",\"webpack://./resources/css/layout.css\",\"webpack://./resources/css/code.css\",\"webpack://./resources/css/h-card.css\",\"webpack://./resources/css/content.css\"],\"names\":[],\"mappings\":\"AAAA,MAEE,0DAA6D,CAC7D,mDAAsD,CACtD,6EAAgF,CAGhF,sBAAuB,CACvB,qBAAsB,CACtB,sBAAuB,CACvB,qBAAsB,CACtB,sBAAuB,CACvB,oBAAqB,CACrB,wBAAyB,CAGzB,uBAA4C,CAC5C,yBAA8C,CAC9C,oBAA2C,CAC3C,4BAAkD,CAClD,uCACF,CArBA,0CAAA,MAgBE,yDAA4C,CAC5C,2DAA8C,CAC9C,sDAA2C,CAC3C,8DAAkD,CAClD,oEACF,CAAA,CArBA,gCAAA,MAgBE,2CAA4C,CAC5C,6CAA8C,CAC9C,0CAA2C,CAC3C,iDAAkD,CAClD,yDACF,CAAA,CCrBA,KCCE,uCAAwC,CACxC,0BAA2B,CDD3B,mCAAoC,CACpC,6BACF,CAEA,KACE,wCACF,CAEA,kBAME,uCACF,CEhBA,MACE,YAAa,CACb,iCAAkC,CAClC,8DAA+C,CAA/C,8CAA+C,CAC/C,YACF,CAEA,aACE,eAAkB,CAClB,YACF,CAEA,KAEE,YACF,CAEA,YAJE,eAWF,CAPA,OAEE,YAKF,CAHE,iBACE,cACF,CDlBF,EACE,uBAKF,CAHE,UACE,+BACF,CAIA,uBACE,uBACF,CEhBF,MACE,mBACF,CCDE,mBAWE,2BAAsB,CAAtB,4BAAsB,CAJtB,uCAAwC,CAFxC,kBAAmB,CACnB,kEAA2D,CAA3D,0DAA2D,CAL3D,YAAa,CAUb,yBAAsB,CAAtB,qBAAsB,CACtB,SAAU,CAFV,SAAU,CANV,YAAa,CAFb,iBAAkB,CAOlB,0CAAoC,CAApC,kCAAoC,CADpC,yBAAkB,CAAlB,sBAAkB,CAAlB,iBAAkB,CALlB,WAmBF,CARE,4BACE,cACF,CAEA,gCAEE,WAAY,CADZ,UAEF,CAIA,yBACE,mBAAa,CAAb,mBAAa,CAAb,YAAa,CACb,SACF,CC3BJ,SACE,mDAAmD,CACnD,2BAA2B,CAD3B,kDAAmD,CACnD,0BA8BF,CA5BE,mBACE,iBACF,CAGE,sBACE,oBACF,CAGF,wBAEE,6BAAmB,CAAnB,4BAAmB,CADnB,mBAAa,CAAb,mBAAa,CAAb,YAAa,CACb,sBAAmB,CAAnB,kBAAmB,CACnB,QAcF,CAZE,2CACE,6BAAmB,CAAnB,4BAAmB,CAAnB,sBAAmB,CAAnB,kBAUF,CARE,6CACE,oBAMF,CAJE,iDAEE,WAAY,CADZ,UAEF\",\"sourcesContent\":[\":root {\\n /* Font Family */\\n --font-family-headings: \\\"Archer SSm A\\\", \\\"Archer SSm B\\\", serif;\\n --font-family-body: \\\"Verlag A\\\", \\\"Verlag B\\\", sans-serif;\\n --font-family-monospace: \\\"Operator Mono SSm A\\\", \\\"Operator Mono SSm B\\\", monospace;\\n\\n /* Font Size */\\n --font-size-sm: 0.75rem; /* 12px */\\n --font-size-base: 1rem; /* 16px, base */\\n --font-size-md: 1.25rem; /* 20px */\\n --font-size-lg: 1.5rem; /* 24px */\\n --font-size-xl: 1.75rem; /* 28px */\\n --font-size-xxl: 2rem; /* 32px */\\n --font-size-xxxl: 2.25rem; /* 36px */\\n\\n /* Colours */\\n --color-primary: oklch(36.8% 0.1 125.505deg);\\n --color-secondary: oklch(96.3% 0.1 125.505deg);\\n --color-link: oklch(48.09% 0.146 241.41deg);\\n --color-link-visited: oklch(70.44% 0.21 304.41deg);\\n --color-primary-shadow: oklch(19.56% 0.054 125.505deg / 40%);\\n}\\n\",\"body {\\n font-family: var(--font-family-body);\\n font-size: var(--font-size-md);\\n}\\n\\ncode {\\n font-family: var(--font-family-monospace);\\n}\\n\\nh1,\\nh2,\\nh3,\\nh4,\\nh5,\\nh6 {\\n font-family: var(--font-family-headings);\\n}\\n\",\"body {\\n background-color: var(--color-secondary);\\n color: var(--color-primary);\\n}\\n\\na {\\n color: var(--color-link);\\n\\n &:visited {\\n color: var(--color-link-visited);\\n }\\n}\\n\\n#site-header {\\n & a:visited {\\n color: var(--color-link);\\n }\\n}\\n\",\".grid {\\n display: grid;\\n grid-template-columns: 5vw 1fr 5vw;\\n grid-template-rows: min-content 1fr min-content;\\n row-gap: 1rem;\\n}\\n\\n#site-header {\\n grid-column: 2 / 3;\\n grid-row: 1 / 2;\\n}\\n\\nmain {\\n grid-column: 2 / 3;\\n grid-row: 2 / 3;\\n}\\n\\nfooter {\\n grid-column: 2 / 3;\\n grid-row: 3 / 4;\\n\\n & .iwc-logo {\\n max-width: 85vw;\\n }\\n}\\n\",\".hljs {\\n border-radius: .5rem;\\n}\\n\",\".h-card {\\n & .hovercard {\\n display: none;\\n position: absolute;\\n z-index: 100;\\n padding: 1rem;\\n border-radius: 1rem;\\n box-shadow: 0 .5rem .5rem .5rem var(--color-primary-shadow);\\n background-color: var(--color-secondary);\\n width: fit-content;\\n transition: opacity 0.5s ease-in-out;\\n opacity: 0;\\n flex-direction: column;\\n gap: .5rem;\\n\\n & .u-photo {\\n max-width: 6rem;\\n }\\n\\n & .social-icon {\\n width: 1rem;\\n height: 1rem;\\n }\\n }\\n\\n &:hover {\\n & .hovercard {\\n display: flex;\\n opacity: 1;\\n }\\n }\\n}\\n\",\"@import url('h-card.css');\\n\\n.h-entry {\\n border-inline-start: 1px solid var(--color-primary);\\n padding-inline-start: .5rem;\\n\\n & .reply-to {\\n font-style: italic;\\n }\\n\\n & .post-info {\\n & a {\\n text-decoration: none;\\n }\\n }\\n\\n & .note-metadata {\\n display: flex;\\n flex-direction: row;\\n gap: 1rem;\\n\\n & .syndication-links {\\n flex-flow: row wrap;\\n\\n & a {\\n text-decoration: none;\\n\\n & svg {\\n width: 1rem;\\n height: 1rem;\\n }\\n }\\n }\\n }\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n/* harmony default export */ __webpack_exports__[\"default\"] = (___CSS_LOADER_EXPORT___);\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvY3NzLWxvYWRlci9kaXN0L2Nqcy5qcz8/cnVsZVNldFsxXS5ydWxlc1sxXS51c2VbMV0hLi9ub2RlX21vZHVsZXMvcG9zdGNzcy1sb2FkZXIvZGlzdC9janMuanM/P3J1bGVTZXRbMV0ucnVsZXNbMV0udXNlWzJdIS4vcmVzb3VyY2VzL2Nzcy9hcHAuY3NzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQUE7QUFDNkc7QUFDakI7QUFDNUYsOEJBQThCLG1GQUEyQixDQUFDLDRGQUFxQztBQUMvRjtBQUNBLGdEQUFnRCwrREFBK0Qsd0RBQXdELGtGQUFrRix1QkFBdUIsc0JBQXNCLHVCQUF1QixzQkFBc0IsdUJBQXVCLHFCQUFxQix5QkFBeUIsd0JBQXdCLDBCQUEwQixxQkFBcUIsNkJBQTZCLHdDQUF3QywwQ0FBMEMsTUFBTSwwREFBMEQsNERBQTRELHVEQUF1RCwrREFBK0Qsc0VBQXNFLGdDQUFnQyxNQUFNLDRDQUE0Qyw4Q0FBOEMsMkNBQTJDLGtEQUFrRCwyREFBMkQsS0FBSyx3Q0FBd0MsMkJBQTJCLG9DQUFvQyw4QkFBOEIsS0FBSyx5Q0FBeUMsa0JBQWtCLHdDQUF3QyxNQUFNLGFBQWEsa0NBQWtDLCtEQUErRCwrQ0FBK0MsYUFBYSxhQUFhLGdCQUFnQixhQUFhLEtBQUssYUFBYSxZQUFZLGdCQUFnQixPQUFPLGFBQWEsaUJBQWlCLGVBQWUsRUFBRSx3QkFBd0IsVUFBVSxnQ0FBZ0MsdUJBQXVCLHdCQUF3QixNQUFNLG9CQUFvQixtQkFBbUIsNEJBQTRCLDZCQUE2Qix3Q0FBd0MsbUJBQW1CLG1FQUFtRSwyREFBMkQsYUFBYSwwQkFBMEIsc0JBQXNCLFVBQVUsVUFBVSxhQUFhLGtCQUFrQiwyQ0FBMkMsbUNBQW1DLDBCQUEwQix1QkFBdUIsa0JBQWtCLFlBQVksNEJBQTRCLGVBQWUsZ0NBQWdDLFlBQVksV0FBVyx5QkFBeUIsb0JBQW9CLG9CQUFvQixhQUFhLFVBQVUsU0FBUyxvREFBb0QsNEJBQTRCLG1EQUFtRCwyQkFBMkIsbUJBQW1CLGtCQUFrQixzQkFBc0IscUJBQXFCLHdCQUF3Qiw4QkFBOEIsNkJBQTZCLG9CQUFvQixvQkFBb0IsYUFBYSx1QkFBdUIsbUJBQW1CLFNBQVMsMkNBQTJDLDhCQUE4Qiw2QkFBNkIsdUJBQXVCLG1CQUFtQiw2Q0FBNkMscUJBQXFCLGlEQUFpRCxZQUFZLFdBQVcsT0FBTyxzbURBQXNtRCwyRkFBMkYsK0RBQStELHlGQUF5RixvREFBb0QseUNBQXlDLCtDQUErQyx5Q0FBeUMseUNBQXlDLHlDQUF5Qyx5Q0FBeUMsOEVBQThFLG1EQUFtRCxnREFBZ0QsdURBQXVELGlFQUFpRSxHQUFHLFdBQVcseUNBQXlDLG1DQUFtQyxHQUFHLFVBQVUsOENBQThDLEdBQUcsaUNBQWlDLDZDQUE2QyxHQUFHLFdBQVcsNkNBQTZDLGdDQUFnQyxHQUFHLE9BQU8sNkJBQTZCLGlCQUFpQix1Q0FBdUMsS0FBSyxHQUFHLGtCQUFrQixpQkFBaUIsK0JBQStCLEtBQUssR0FBRyxZQUFZLGtCQUFrQix1Q0FBdUMsb0RBQW9ELGtCQUFrQixHQUFHLGtCQUFrQix1QkFBdUIsb0JBQW9CLEdBQUcsVUFBVSx1QkFBdUIsb0JBQW9CLEdBQUcsWUFBWSx1QkFBdUIsb0JBQW9CLG1CQUFtQixzQkFBc0IsS0FBSyxHQUFHLFlBQVkseUJBQXlCLEdBQUcsY0FBYyxrQkFBa0Isb0JBQW9CLHlCQUF5QixtQkFBbUIsb0JBQW9CLDBCQUEwQixrRUFBa0UsK0NBQStDLHlCQUF5QiwyQ0FBMkMsaUJBQWlCLDZCQUE2QixpQkFBaUIsb0JBQW9CLHdCQUF3QixPQUFPLHdCQUF3QixvQkFBb0IscUJBQXFCLE9BQU8sS0FBSyxlQUFlLG9CQUFvQixzQkFBc0IsbUJBQW1CLE9BQU8sS0FBSyxHQUFHLCtCQUErQixjQUFjLHdEQUF3RCxnQ0FBZ0MsbUJBQW1CLHlCQUF5QixLQUFLLG9CQUFvQixXQUFXLDhCQUE4QixPQUFPLEtBQUssd0JBQXdCLG9CQUFvQiwwQkFBMEIsZ0JBQWdCLDhCQUE4Qiw0QkFBNEIsZUFBZSxnQ0FBZ0MsbUJBQW1CLHdCQUF3Qix5QkFBeUIsV0FBVyxTQUFTLE9BQU8sS0FBSyxHQUFHLHFCQUFxQjtBQUN6c1A7QUFDQSwrREFBZSx1QkFBdUIsRUFBQyIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9yZXNvdXJjZXMvY3NzL2FwcC5jc3M/MGJhMSJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBJbXBvcnRzXG5pbXBvcnQgX19fQ1NTX0xPQURFUl9BUElfU09VUkNFTUFQX0lNUE9SVF9fXyBmcm9tIFwiLi4vLi4vbm9kZV9tb2R1bGVzL2Nzcy1sb2FkZXIvZGlzdC9ydW50aW1lL3NvdXJjZU1hcHMuanNcIjtcbmltcG9ydCBfX19DU1NfTE9BREVSX0FQSV9JTVBPUlRfX18gZnJvbSBcIi4uLy4uL25vZGVfbW9kdWxlcy9jc3MtbG9hZGVyL2Rpc3QvcnVudGltZS9hcGkuanNcIjtcbnZhciBfX19DU1NfTE9BREVSX0VYUE9SVF9fXyA9IF9fX0NTU19MT0FERVJfQVBJX0lNUE9SVF9fXyhfX19DU1NfTE9BREVSX0FQSV9TT1VSQ0VNQVBfSU1QT1JUX19fKTtcbi8vIE1vZHVsZVxuX19fQ1NTX0xPQURFUl9FWFBPUlRfX18ucHVzaChbbW9kdWxlLmlkLCBcIjpyb290ey0tZm9udC1mYW1pbHktaGVhZGluZ3M6XFxcIkFyY2hlciBTU20gQVxcXCIsXFxcIkFyY2hlciBTU20gQlxcXCIsc2VyaWY7LS1mb250LWZhbWlseS1ib2R5OlxcXCJWZXJsYWcgQVxcXCIsXFxcIlZlcmxhZyBCXFxcIixzYW5zLXNlcmlmOy0tZm9udC1mYW1pbHktbW9ub3NwYWNlOlxcXCJPcGVyYXRvciBNb25vIFNTbSBBXFxcIixcXFwiT3BlcmF0b3IgTW9ubyBTU20gQlxcXCIsbW9ub3NwYWNlOy0tZm9udC1zaXplLXNtOjAuNzVyZW07LS1mb250LXNpemUtYmFzZToxcmVtOy0tZm9udC1zaXplLW1kOjEuMjVyZW07LS1mb250LXNpemUtbGc6MS41cmVtOy0tZm9udC1zaXplLXhsOjEuNzVyZW07LS1mb250LXNpemUteHhsOjJyZW07LS1mb250LXNpemUteHh4bDoyLjI1cmVtOy0tY29sb3ItcHJpbWFyeTojMzM0NzAwOy0tY29sb3Itc2Vjb25kYXJ5OiNlM2ZmYjc7LS1jb2xvci1saW5rOiMwMDY0OWU7LS1jb2xvci1saW5rLXZpc2l0ZWQ6I2JjN2FmZjstLWNvbG9yLXByaW1hcnktc2hhZG93OnJnYmEoMTYsMjUsMCwuNCl9QHN1cHBvcnRzIChjb2xvcjpjb2xvcihkaXNwbGF5LXAzIDAgMCAwKSl7OnJvb3R7LS1jb2xvci1wcmltYXJ5OmNvbG9yKGRpc3BsYXktcDMgMC4yMTU2NyAwLjI3ODM4IDAuMDM2MTUpOy0tY29sb3Itc2Vjb25kYXJ5OmNvbG9yKGRpc3BsYXktcDMgMC45MTAxNiAwLjk5ODQyIDAuNzQwODIpOy0tY29sb3ItbGluazpjb2xvcihkaXNwbGF5LXAzIDAuMDEwNDUgMC4zODM1MSAwLjYzNjE4KTstLWNvbG9yLWxpbmstdmlzaXRlZDpjb2xvcihkaXNwbGF5LXAzIDAuNzA0NjcgMC40NzU0OSAwLjk5OTU4KTstLWNvbG9yLXByaW1hcnktc2hhZG93OmNvbG9yKGRpc3BsYXktcDMgMC4wNjc2MiAwLjA5NjQ2IDAuMDA0NDEvMC40KX19QHN1cHBvcnRzIChjb2xvcjpva2xjaCgwJSAwIDApKXs6cm9vdHstLWNvbG9yLXByaW1hcnk6b2tsY2goMzYuOCUgMC4xIDEyNS41MDVkZWcpOy0tY29sb3Itc2Vjb25kYXJ5Om9rbGNoKDk2LjMlIDAuMSAxMjUuNTA1ZGVnKTstLWNvbG9yLWxpbms6b2tsY2goNDguMDklIDAuMTQ2IDI0MS40MWRlZyk7LS1jb2xvci1saW5rLXZpc2l0ZWQ6b2tsY2goNzAuNDQlIDAuMjEgMzA0LjQxZGVnKTstLWNvbG9yLXByaW1hcnktc2hhZG93Om9rbGNoKDE5LjU2JSAwLjA1NCAxMjUuNTA1ZGVnLzQwJSl9fWJvZHl7YmFja2dyb3VuZC1jb2xvcjp2YXIoLS1jb2xvci1zZWNvbmRhcnkpO2NvbG9yOnZhcigtLWNvbG9yLXByaW1hcnkpO2ZvbnQtZmFtaWx5OnZhcigtLWZvbnQtZmFtaWx5LWJvZHkpO2ZvbnQtc2l6ZTp2YXIoLS1mb250LXNpemUtbWQpfWNvZGV7Zm9udC1mYW1pbHk6dmFyKC0tZm9udC1mYW1pbHktbW9ub3NwYWNlKX1oMSxoMixoMyxoNCxoNSxoNntmb250LWZhbWlseTp2YXIoLS1mb250LWZhbWlseS1oZWFkaW5ncyl9LmdyaWR7ZGlzcGxheTpncmlkO2dyaWQtdGVtcGxhdGUtY29sdW1uczo1dncgMWZyIDV2dztncmlkLXRlbXBsYXRlLXJvd3M6LXdlYmtpdC1taW4tY29udGVudCAxZnIgLXdlYmtpdC1taW4tY29udGVudDtncmlkLXRlbXBsYXRlLXJvd3M6bWluLWNvbnRlbnQgMWZyIG1pbi1jb250ZW50O3Jvdy1nYXA6MXJlbX0jc2l0ZS1oZWFkZXJ7Z3JpZC1jb2x1bW46Mi8zO2dyaWQtcm93OjEvMn1tYWlue2dyaWQtcm93OjIvM31mb290ZXIsbWFpbntncmlkLWNvbHVtbjoyLzN9Zm9vdGVye2dyaWQtcm93OjMvNH1mb290ZXIgLml3Yy1sb2dve21heC13aWR0aDo4NXZ3fWF7Y29sb3I6dmFyKC0tY29sb3ItbGluayl9YTp2aXNpdGVke2NvbG9yOnZhcigtLWNvbG9yLWxpbmstdmlzaXRlZCl9I3NpdGUtaGVhZGVyIGE6dmlzaXRlZHtjb2xvcjp2YXIoLS1jb2xvci1saW5rKX0uaGxqc3tib3JkZXItcmFkaXVzOi41cmVtfS5oLWNhcmQgLmhvdmVyY2FyZHstd2Via2l0LWJveC1vcmllbnQ6dmVydGljYWw7LXdlYmtpdC1ib3gtZGlyZWN0aW9uOm5vcm1hbDtiYWNrZ3JvdW5kLWNvbG9yOnZhcigtLWNvbG9yLXNlY29uZGFyeSk7Ym9yZGVyLXJhZGl1czoxcmVtOy13ZWJraXQtYm94LXNoYWRvdzowIC41cmVtIC41cmVtIC41cmVtIHZhcigtLWNvbG9yLXByaW1hcnktc2hhZG93KTtib3gtc2hhZG93OjAgLjVyZW0gLjVyZW0gLjVyZW0gdmFyKC0tY29sb3ItcHJpbWFyeS1zaGFkb3cpO2Rpc3BsYXk6bm9uZTstbXMtZmxleC1kaXJlY3Rpb246Y29sdW1uO2ZsZXgtZGlyZWN0aW9uOmNvbHVtbjtnYXA6LjVyZW07b3BhY2l0eTowO3BhZGRpbmc6MXJlbTtwb3NpdGlvbjphYnNvbHV0ZTstd2Via2l0LXRyYW5zaXRpb246b3BhY2l0eSAuNXMgZWFzZS1pbi1vdXQ7dHJhbnNpdGlvbjpvcGFjaXR5IC41cyBlYXNlLWluLW91dDt3aWR0aDotd2Via2l0LWZpdC1jb250ZW50O3dpZHRoOi1tb3otZml0LWNvbnRlbnQ7d2lkdGg6Zml0LWNvbnRlbnQ7ei1pbmRleDoxMDB9LmgtY2FyZCAuaG92ZXJjYXJkIC51LXBob3Rve21heC13aWR0aDo2cmVtfS5oLWNhcmQgLmhvdmVyY2FyZCAuc29jaWFsLWljb257aGVpZ2h0OjFyZW07d2lkdGg6MXJlbX0uaC1jYXJkOmhvdmVyIC5ob3ZlcmNhcmR7ZGlzcGxheTotd2Via2l0LWJveDtkaXNwbGF5Oi1tcy1mbGV4Ym94O2Rpc3BsYXk6ZmxleDtvcGFjaXR5OjF9LmgtZW50cnl7LXdlYmtpdC1ib3JkZXItc3RhcnQ6MXB4IHNvbGlkIHZhcigtLWNvbG9yLXByaW1hcnkpOy13ZWJraXQtcGFkZGluZy1zdGFydDouNXJlbTtib3JkZXItaW5saW5lLXN0YXJ0OjFweCBzb2xpZCB2YXIoLS1jb2xvci1wcmltYXJ5KTtwYWRkaW5nLWlubGluZS1zdGFydDouNXJlbX0uaC1lbnRyeSAucmVwbHktdG97Zm9udC1zdHlsZTppdGFsaWN9LmgtZW50cnkgLnBvc3QtaW5mbyBhe3RleHQtZGVjb3JhdGlvbjpub25lfS5oLWVudHJ5IC5ub3RlLW1ldGFkYXRhey13ZWJraXQtYm94LW9yaWVudDpob3Jpem9udGFsOy13ZWJraXQtYm94LWRpcmVjdGlvbjpub3JtYWw7ZGlzcGxheTotd2Via2l0LWJveDtkaXNwbGF5Oi1tcy1mbGV4Ym94O2Rpc3BsYXk6ZmxleDstbXMtZmxleC1kaXJlY3Rpb246cm93O2ZsZXgtZGlyZWN0aW9uOnJvdztnYXA6MXJlbX0uaC1lbnRyeSAubm90ZS1tZXRhZGF0YSAuc3luZGljYXRpb24tbGlua3N7LXdlYmtpdC1ib3gtb3JpZW50Omhvcml6b250YWw7LXdlYmtpdC1ib3gtZGlyZWN0aW9uOm5vcm1hbDstbXMtZmxleC1mbG93OnJvdyB3cmFwO2ZsZXgtZmxvdzpyb3cgd3JhcH0uaC1lbnRyeSAubm90ZS1tZXRhZGF0YSAuc3luZGljYXRpb24tbGlua3MgYXt0ZXh0LWRlY29yYXRpb246bm9uZX0uaC1lbnRyeSAubm90ZS1tZXRhZGF0YSAuc3luZGljYXRpb24tbGlua3MgYSBzdmd7aGVpZ2h0OjFyZW07d2lkdGg6MXJlbX1cIiwgXCJcIix7XCJ2ZXJzaW9uXCI6MyxcInNvdXJjZXNcIjpbXCJ3ZWJwYWNrOi8vLi9yZXNvdXJjZXMvY3NzL3ZhcmlhYmxlcy5jc3NcIixcIndlYnBhY2s6Ly8uL3Jlc291cmNlcy9jc3MvZm9udHMuY3NzXCIsXCJ3ZWJwYWNrOi8vLi9yZXNvdXJjZXMvY3NzL2NvbG91cnMuY3NzXCIsXCJ3ZWJwYWNrOi8vLi9yZXNvdXJjZXMvY3NzL2xheW91dC5jc3NcIixcIndlYnBhY2s6Ly8uL3Jlc291cmNlcy9jc3MvY29kZS5jc3NcIixcIndlYnBhY2s6Ly8uL3Jlc291cmNlcy9jc3MvaC1jYXJkLmNzc1wiLFwid2VicGFjazovLy4vcmVzb3VyY2VzL2Nzcy9jb250ZW50LmNzc1wiXSxcIm5hbWVzXCI6W10sXCJtYXBwaW5nc1wiOlwiQUFBQSxNQUVFLDBEQUE2RCxDQUM3RCxtREFBc0QsQ0FDdEQsNkVBQWdGLENBR2hGLHNCQUF1QixDQUN2QixxQkFBc0IsQ0FDdEIsc0JBQXVCLENBQ3ZCLHFCQUFzQixDQUN0QixzQkFBdUIsQ0FDdkIsb0JBQXFCLENBQ3JCLHdCQUF5QixDQUd6Qix1QkFBNEMsQ0FDNUMseUJBQThDLENBQzlDLG9CQUEyQyxDQUMzQyw0QkFBa0QsQ0FDbEQsdUNBQ0YsQ0FyQkEsMENBQUEsTUFnQkUseURBQTRDLENBQzVDLDJEQUE4QyxDQUM5QyxzREFBMkMsQ0FDM0MsOERBQWtELENBQ2xELG9FQUNGLENBQUEsQ0FyQkEsZ0NBQUEsTUFnQkUsMkNBQTRDLENBQzVDLDZDQUE4QyxDQUM5QywwQ0FBMkMsQ0FDM0MsaURBQWtELENBQ2xELHlEQUNGLENBQUEsQ0NyQkEsS0NDRSx1Q0FBd0MsQ0FDeEMsMEJBQTJCLENERDNCLG1DQUFvQyxDQUNwQyw2QkFDRixDQUVBLEtBQ0Usd0NBQ0YsQ0FFQSxrQkFNRSx1Q0FDRixDRWhCQSxNQUNFLFlBQWEsQ0FDYixpQ0FBa0MsQ0FDbEMsOERBQStDLENBQS9DLDhDQUErQyxDQUMvQyxZQUNGLENBRUEsYUFDRSxlQUFrQixDQUNsQixZQUNGLENBRUEsS0FFRSxZQUNGLENBRUEsWUFKRSxlQVdGLENBUEEsT0FFRSxZQUtGLENBSEUsaUJBQ0UsY0FDRixDRGxCRixFQUNFLHVCQUtGLENBSEUsVUFDRSwrQkFDRixDQUlBLHVCQUNFLHVCQUNGLENFaEJGLE1BQ0UsbUJBQ0YsQ0NERSxtQkFXRSwyQkFBc0IsQ0FBdEIsNEJBQXNCLENBSnRCLHVDQUF3QyxDQUZ4QyxrQkFBbUIsQ0FDbkIsa0VBQTJELENBQTNELDBEQUEyRCxDQUwzRCxZQUFhLENBVWIseUJBQXNCLENBQXRCLHFCQUFzQixDQUN0QixTQUFVLENBRlYsU0FBVSxDQU5WLFlBQWEsQ0FGYixpQkFBa0IsQ0FPbEIsMENBQW9DLENBQXBDLGtDQUFvQyxDQURwQyx5QkFBa0IsQ0FBbEIsc0JBQWtCLENBQWxCLGlCQUFrQixDQUxsQixXQW1CRixDQVJFLDRCQUNFLGNBQ0YsQ0FFQSxnQ0FFRSxXQUFZLENBRFosVUFFRixDQUlBLHlCQUNFLG1CQUFhLENBQWIsbUJBQWEsQ0FBYixZQUFhLENBQ2IsU0FDRixDQzNCSixTQUNFLG1EQUFtRCxDQUNuRCwyQkFBMkIsQ0FEM0Isa0RBQW1ELENBQ25ELDBCQThCRixDQTVCRSxtQkFDRSxpQkFDRixDQUdFLHNCQUNFLG9CQUNGLENBR0Ysd0JBRUUsNkJBQW1CLENBQW5CLDRCQUFtQixDQURuQixtQkFBYSxDQUFiLG1CQUFhLENBQWIsWUFBYSxDQUNiLHNCQUFtQixDQUFuQixrQkFBbUIsQ0FDbkIsUUFjRixDQVpFLDJDQUNFLDZCQUFtQixDQUFuQiw0QkFBbUIsQ0FBbkIsc0JBQW1CLENBQW5CLGtCQVVGLENBUkUsNkNBQ0Usb0JBTUYsQ0FKRSxpREFFRSxXQUFZLENBRFosVUFFRlwiLFwic291cmNlc0NvbnRlbnRcIjpbXCI6cm9vdCB7XFxuICAvKiBGb250IEZhbWlseSAqL1xcbiAgLS1mb250LWZhbWlseS1oZWFkaW5nczogXFxcIkFyY2hlciBTU20gQVxcXCIsIFxcXCJBcmNoZXIgU1NtIEJcXFwiLCBzZXJpZjtcXG4gIC0tZm9udC1mYW1pbHktYm9keTogXFxcIlZlcmxhZyBBXFxcIiwgXFxcIlZlcmxhZyBCXFxcIiwgc2Fucy1zZXJpZjtcXG4gIC0tZm9udC1mYW1pbHktbW9ub3NwYWNlOiBcXFwiT3BlcmF0b3IgTW9ubyBTU20gQVxcXCIsIFxcXCJPcGVyYXRvciBNb25vIFNTbSBCXFxcIiwgbW9ub3NwYWNlO1xcblxcbiAgLyogRm9udCBTaXplICovXFxuICAtLWZvbnQtc2l6ZS1zbTogMC43NXJlbTsgICAvKiAxMnB4ICovXFxuICAtLWZvbnQtc2l6ZS1iYXNlOiAxcmVtOyAgICAvKiAxNnB4LCBiYXNlICovXFxuICAtLWZvbnQtc2l6ZS1tZDogMS4yNXJlbTsgICAvKiAyMHB4ICovXFxuICAtLWZvbnQtc2l6ZS1sZzogMS41cmVtOyAgICAvKiAyNHB4ICovXFxuICAtLWZvbnQtc2l6ZS14bDogMS43NXJlbTsgICAvKiAyOHB4ICovXFxuICAtLWZvbnQtc2l6ZS14eGw6IDJyZW07ICAgICAvKiAzMnB4ICovXFxuICAtLWZvbnQtc2l6ZS14eHhsOiAyLjI1cmVtOyAvKiAzNnB4ICovXFxuXFxuICAvKiBDb2xvdXJzICovXFxuICAtLWNvbG9yLXByaW1hcnk6IG9rbGNoKDM2LjglIDAuMSAxMjUuNTA1ZGVnKTtcXG4gIC0tY29sb3Itc2Vjb25kYXJ5OiBva2xjaCg5Ni4zJSAwLjEgMTI1LjUwNWRlZyk7XFxuICAtLWNvbG9yLWxpbms6IG9rbGNoKDQ4LjA5JSAwLjE0NiAyNDEuNDFkZWcpO1xcbiAgLS1jb2xvci1saW5rLXZpc2l0ZWQ6IG9rbGNoKDcwLjQ0JSAwLjIxIDMwNC40MWRlZyk7XFxuICAtLWNvbG9yLXByaW1hcnktc2hhZG93OiBva2xjaCgxOS41NiUgMC4wNTQgMTI1LjUwNWRlZyAvIDQwJSk7XFxufVxcblwiLFwiYm9keSB7XFxuICBmb250LWZhbWlseTogdmFyKC0tZm9udC1mYW1pbHktYm9keSk7XFxuICBmb250LXNpemU6IHZhcigtLWZvbnQtc2l6ZS1tZCk7XFxufVxcblxcbmNvZGUge1xcbiAgZm9udC1mYW1pbHk6IHZhcigtLWZvbnQtZmFtaWx5LW1vbm9zcGFjZSk7XFxufVxcblxcbmgxLFxcbmgyLFxcbmgzLFxcbmg0LFxcbmg1LFxcbmg2IHtcXG4gIGZvbnQtZmFtaWx5OiB2YXIoLS1mb250LWZhbWlseS1oZWFkaW5ncyk7XFxufVxcblwiLFwiYm9keSB7XFxuICBiYWNrZ3JvdW5kLWNvbG9yOiB2YXIoLS1jb2xvci1zZWNvbmRhcnkpO1xcbiAgY29sb3I6IHZhcigtLWNvbG9yLXByaW1hcnkpO1xcbn1cXG5cXG5hIHtcXG4gIGNvbG9yOiB2YXIoLS1jb2xvci1saW5rKTtcXG5cXG4gICY6dmlzaXRlZCB7XFxuICAgIGNvbG9yOiB2YXIoLS1jb2xvci1saW5rLXZpc2l0ZWQpO1xcbiAgfVxcbn1cXG5cXG4jc2l0ZS1oZWFkZXIge1xcbiAgJiBhOnZpc2l0ZWQge1xcbiAgICBjb2xvcjogdmFyKC0tY29sb3ItbGluayk7XFxuICB9XFxufVxcblwiLFwiLmdyaWQge1xcbiAgZGlzcGxheTogZ3JpZDtcXG4gIGdyaWQtdGVtcGxhdGUtY29sdW1uczogNXZ3IDFmciA1dnc7XFxuICBncmlkLXRlbXBsYXRlLXJvd3M6IG1pbi1jb250ZW50IDFmciBtaW4tY29udGVudDtcXG4gIHJvdy1nYXA6IDFyZW07XFxufVxcblxcbiNzaXRlLWhlYWRlciB7XFxuICBncmlkLWNvbHVtbjogMiAvIDM7XFxuICBncmlkLXJvdzogMSAvIDI7XFxufVxcblxcbm1haW4ge1xcbiAgZ3JpZC1jb2x1bW46IDIgLyAzO1xcbiAgZ3JpZC1yb3c6IDIgLyAzO1xcbn1cXG5cXG5mb290ZXIge1xcbiAgZ3JpZC1jb2x1bW46IDIgLyAzO1xcbiAgZ3JpZC1yb3c6IDMgLyA0O1xcblxcbiAgJiAuaXdjLWxvZ28ge1xcbiAgICBtYXgtd2lkdGg6IDg1dnc7XFxuICB9XFxufVxcblwiLFwiLmhsanMge1xcbiAgYm9yZGVyLXJhZGl1czogLjVyZW07XFxufVxcblwiLFwiLmgtY2FyZCB7XFxuICAmIC5ob3ZlcmNhcmQge1xcbiAgICBkaXNwbGF5OiBub25lO1xcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XFxuICAgIHotaW5kZXg6IDEwMDtcXG4gICAgcGFkZGluZzogMXJlbTtcXG4gICAgYm9yZGVyLXJhZGl1czogMXJlbTtcXG4gICAgYm94LXNoYWRvdzogMCAuNXJlbSAuNXJlbSAuNXJlbSB2YXIoLS1jb2xvci1wcmltYXJ5LXNoYWRvdyk7XFxuICAgIGJhY2tncm91bmQtY29sb3I6IHZhcigtLWNvbG9yLXNlY29uZGFyeSk7XFxuICAgIHdpZHRoOiBmaXQtY29udGVudDtcXG4gICAgdHJhbnNpdGlvbjogb3BhY2l0eSAwLjVzIGVhc2UtaW4tb3V0O1xcbiAgICBvcGFjaXR5OiAwO1xcbiAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xcbiAgICBnYXA6IC41cmVtO1xcblxcbiAgICAmIC51LXBob3RvIHtcXG4gICAgICBtYXgtd2lkdGg6IDZyZW07XFxuICAgIH1cXG5cXG4gICAgJiAuc29jaWFsLWljb24ge1xcbiAgICAgIHdpZHRoOiAxcmVtO1xcbiAgICAgIGhlaWdodDogMXJlbTtcXG4gICAgfVxcbiAgfVxcblxcbiAgJjpob3ZlciB7XFxuICAgICYgLmhvdmVyY2FyZCB7XFxuICAgICAgZGlzcGxheTogZmxleDtcXG4gICAgICBvcGFjaXR5OiAxO1xcbiAgICB9XFxuICB9XFxufVxcblwiLFwiQGltcG9ydCB1cmwoJ2gtY2FyZC5jc3MnKTtcXG5cXG4uaC1lbnRyeSB7XFxuICBib3JkZXItaW5saW5lLXN0YXJ0OiAxcHggc29saWQgdmFyKC0tY29sb3ItcHJpbWFyeSk7XFxuICBwYWRkaW5nLWlubGluZS1zdGFydDogLjVyZW07XFxuXFxuICAmIC5yZXBseS10byB7XFxuICAgIGZvbnQtc3R5bGU6IGl0YWxpYztcXG4gIH1cXG5cXG4gICYgLnBvc3QtaW5mbyB7XFxuICAgICYgYSB7XFxuICAgICAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xcbiAgICB9XFxuICB9XFxuXFxuICAmIC5ub3RlLW1ldGFkYXRhIHtcXG4gICAgZGlzcGxheTogZmxleDtcXG4gICAgZmxleC1kaXJlY3Rpb246IHJvdztcXG4gICAgZ2FwOiAxcmVtO1xcblxcbiAgICAmIC5zeW5kaWNhdGlvbi1saW5rcyB7XFxuICAgICAgZmxleC1mbG93OiByb3cgd3JhcDtcXG5cXG4gICAgICAmIGEge1xcbiAgICAgICAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xcblxcbiAgICAgICAgJiBzdmcge1xcbiAgICAgICAgICB3aWR0aDogMXJlbTtcXG4gICAgICAgICAgaGVpZ2h0OiAxcmVtO1xcbiAgICAgICAgfVxcbiAgICAgIH1cXG4gICAgfVxcbiAgfVxcbn1cXG5cIl0sXCJzb3VyY2VSb290XCI6XCJcIn1dKTtcbi8vIEV4cG9ydHNcbmV4cG9ydCBkZWZhdWx0IF9fX0NTU19MT0FERVJfRVhQT1JUX19fO1xuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./resources/css/app.css\n"); - -/***/ }), - -/***/ "./node_modules/css-loader/dist/runtime/api.js": -/*!*****************************************************!*\ - !*** ./node_modules/css-loader/dist/runtime/api.js ***! - \*****************************************************/ -/***/ (function(module) { - -eval("\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\nmodule.exports = function (cssWithMappingToString) {\n var list = [];\n\n // return the list of modules as css string\n list.toString = function toString() {\n return this.map(function (item) {\n var content = \"\";\n var needLayer = typeof item[5] !== \"undefined\";\n if (item[4]) {\n content += \"@supports (\".concat(item[4], \") {\");\n }\n if (item[2]) {\n content += \"@media \".concat(item[2], \" {\");\n }\n if (needLayer) {\n content += \"@layer\".concat(item[5].length > 0 ? \" \".concat(item[5]) : \"\", \" {\");\n }\n content += cssWithMappingToString(item);\n if (needLayer) {\n content += \"}\";\n }\n if (item[2]) {\n content += \"}\";\n }\n if (item[4]) {\n content += \"}\";\n }\n return content;\n }).join(\"\");\n };\n\n // import a list of modules into the list\n list.i = function i(modules, media, dedupe, supports, layer) {\n if (typeof modules === \"string\") {\n modules = [[null, modules, undefined]];\n }\n var alreadyImportedModules = {};\n if (dedupe) {\n for (var k = 0; k < this.length; k++) {\n var id = this[k][0];\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n }\n for (var _k = 0; _k < modules.length; _k++) {\n var item = [].concat(modules[_k]);\n if (dedupe && alreadyImportedModules[item[0]]) {\n continue;\n }\n if (typeof layer !== \"undefined\") {\n if (typeof item[5] === \"undefined\") {\n item[5] = layer;\n } else {\n item[1] = \"@layer\".concat(item[5].length > 0 ? \" \".concat(item[5]) : \"\", \" {\").concat(item[1], \"}\");\n item[5] = layer;\n }\n }\n if (media) {\n if (!item[2]) {\n item[2] = media;\n } else {\n item[1] = \"@media \".concat(item[2], \" {\").concat(item[1], \"}\");\n item[2] = media;\n }\n }\n if (supports) {\n if (!item[4]) {\n item[4] = \"\".concat(supports);\n } else {\n item[1] = \"@supports (\".concat(item[4], \") {\").concat(item[1], \"}\");\n item[4] = supports;\n }\n }\n list.push(item);\n }\n };\n return list;\n};//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvY3NzLWxvYWRlci9kaXN0L3J1bnRpbWUvYXBpLmpzIiwibWFwcGluZ3MiOiJBQUFhOztBQUViO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxREFBcUQ7QUFDckQ7QUFDQTtBQUNBLGdEQUFnRDtBQUNoRDtBQUNBO0FBQ0EscUZBQXFGO0FBQ3JGO0FBQ0E7QUFDQTtBQUNBLHFCQUFxQjtBQUNyQjtBQUNBO0FBQ0EscUJBQXFCO0FBQ3JCO0FBQ0E7QUFDQSxxQkFBcUI7QUFDckI7QUFDQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixpQkFBaUI7QUFDdkM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUJBQXFCLHFCQUFxQjtBQUMxQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVixzRkFBc0YscUJBQXFCO0FBQzNHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVixpREFBaUQscUJBQXFCO0FBQ3RFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVixzREFBc0QscUJBQXFCO0FBQzNFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9qYnVrLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL2Nzcy1sb2FkZXIvZGlzdC9ydW50aW1lL2FwaS5qcz8yNGZiIl0sInNvdXJjZXNDb250ZW50IjpbIlwidXNlIHN0cmljdFwiO1xuXG4vKlxuICBNSVQgTGljZW5zZSBodHRwOi8vd3d3Lm9wZW5zb3VyY2Uub3JnL2xpY2Vuc2VzL21pdC1saWNlbnNlLnBocFxuICBBdXRob3IgVG9iaWFzIEtvcHBlcnMgQHNva3JhXG4qL1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY3NzV2l0aE1hcHBpbmdUb1N0cmluZykge1xuICB2YXIgbGlzdCA9IFtdO1xuXG4gIC8vIHJldHVybiB0aGUgbGlzdCBvZiBtb2R1bGVzIGFzIGNzcyBzdHJpbmdcbiAgbGlzdC50b1N0cmluZyA9IGZ1bmN0aW9uIHRvU3RyaW5nKCkge1xuICAgIHJldHVybiB0aGlzLm1hcChmdW5jdGlvbiAoaXRlbSkge1xuICAgICAgdmFyIGNvbnRlbnQgPSBcIlwiO1xuICAgICAgdmFyIG5lZWRMYXllciA9IHR5cGVvZiBpdGVtWzVdICE9PSBcInVuZGVmaW5lZFwiO1xuICAgICAgaWYgKGl0ZW1bNF0pIHtcbiAgICAgICAgY29udGVudCArPSBcIkBzdXBwb3J0cyAoXCIuY29uY2F0KGl0ZW1bNF0sIFwiKSB7XCIpO1xuICAgICAgfVxuICAgICAgaWYgKGl0ZW1bMl0pIHtcbiAgICAgICAgY29udGVudCArPSBcIkBtZWRpYSBcIi5jb25jYXQoaXRlbVsyXSwgXCIge1wiKTtcbiAgICAgIH1cbiAgICAgIGlmIChuZWVkTGF5ZXIpIHtcbiAgICAgICAgY29udGVudCArPSBcIkBsYXllclwiLmNvbmNhdChpdGVtWzVdLmxlbmd0aCA+IDAgPyBcIiBcIi5jb25jYXQoaXRlbVs1XSkgOiBcIlwiLCBcIiB7XCIpO1xuICAgICAgfVxuICAgICAgY29udGVudCArPSBjc3NXaXRoTWFwcGluZ1RvU3RyaW5nKGl0ZW0pO1xuICAgICAgaWYgKG5lZWRMYXllcikge1xuICAgICAgICBjb250ZW50ICs9IFwifVwiO1xuICAgICAgfVxuICAgICAgaWYgKGl0ZW1bMl0pIHtcbiAgICAgICAgY29udGVudCArPSBcIn1cIjtcbiAgICAgIH1cbiAgICAgIGlmIChpdGVtWzRdKSB7XG4gICAgICAgIGNvbnRlbnQgKz0gXCJ9XCI7XG4gICAgICB9XG4gICAgICByZXR1cm4gY29udGVudDtcbiAgICB9KS5qb2luKFwiXCIpO1xuICB9O1xuXG4gIC8vIGltcG9ydCBhIGxpc3Qgb2YgbW9kdWxlcyBpbnRvIHRoZSBsaXN0XG4gIGxpc3QuaSA9IGZ1bmN0aW9uIGkobW9kdWxlcywgbWVkaWEsIGRlZHVwZSwgc3VwcG9ydHMsIGxheWVyKSB7XG4gICAgaWYgKHR5cGVvZiBtb2R1bGVzID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBtb2R1bGVzID0gW1tudWxsLCBtb2R1bGVzLCB1bmRlZmluZWRdXTtcbiAgICB9XG4gICAgdmFyIGFscmVhZHlJbXBvcnRlZE1vZHVsZXMgPSB7fTtcbiAgICBpZiAoZGVkdXBlKSB7XG4gICAgICBmb3IgKHZhciBrID0gMDsgayA8IHRoaXMubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgdmFyIGlkID0gdGhpc1trXVswXTtcbiAgICAgICAgaWYgKGlkICE9IG51bGwpIHtcbiAgICAgICAgICBhbHJlYWR5SW1wb3J0ZWRNb2R1bGVzW2lkXSA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgZm9yICh2YXIgX2sgPSAwOyBfayA8IG1vZHVsZXMubGVuZ3RoOyBfaysrKSB7XG4gICAgICB2YXIgaXRlbSA9IFtdLmNvbmNhdChtb2R1bGVzW19rXSk7XG4gICAgICBpZiAoZGVkdXBlICYmIGFscmVhZHlJbXBvcnRlZE1vZHVsZXNbaXRlbVswXV0pIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIGxheWVyICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgIGlmICh0eXBlb2YgaXRlbVs1XSA9PT0gXCJ1bmRlZmluZWRcIikge1xuICAgICAgICAgIGl0ZW1bNV0gPSBsYXllcjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpdGVtWzFdID0gXCJAbGF5ZXJcIi5jb25jYXQoaXRlbVs1XS5sZW5ndGggPiAwID8gXCIgXCIuY29uY2F0KGl0ZW1bNV0pIDogXCJcIiwgXCIge1wiKS5jb25jYXQoaXRlbVsxXSwgXCJ9XCIpO1xuICAgICAgICAgIGl0ZW1bNV0gPSBsYXllcjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKG1lZGlhKSB7XG4gICAgICAgIGlmICghaXRlbVsyXSkge1xuICAgICAgICAgIGl0ZW1bMl0gPSBtZWRpYTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpdGVtWzFdID0gXCJAbWVkaWEgXCIuY29uY2F0KGl0ZW1bMl0sIFwiIHtcIikuY29uY2F0KGl0ZW1bMV0sIFwifVwiKTtcbiAgICAgICAgICBpdGVtWzJdID0gbWVkaWE7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChzdXBwb3J0cykge1xuICAgICAgICBpZiAoIWl0ZW1bNF0pIHtcbiAgICAgICAgICBpdGVtWzRdID0gXCJcIi5jb25jYXQoc3VwcG9ydHMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGl0ZW1bMV0gPSBcIkBzdXBwb3J0cyAoXCIuY29uY2F0KGl0ZW1bNF0sIFwiKSB7XCIpLmNvbmNhdChpdGVtWzFdLCBcIn1cIik7XG4gICAgICAgICAgaXRlbVs0XSA9IHN1cHBvcnRzO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBsaXN0LnB1c2goaXRlbSk7XG4gICAgfVxuICB9O1xuICByZXR1cm4gbGlzdDtcbn07Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/css-loader/dist/runtime/api.js\n"); - -/***/ }), - -/***/ "./node_modules/css-loader/dist/runtime/sourceMaps.js": -/*!************************************************************!*\ - !*** ./node_modules/css-loader/dist/runtime/sourceMaps.js ***! - \************************************************************/ -/***/ (function(module) { - -eval("\n\nmodule.exports = function (item) {\n var content = item[1];\n var cssMapping = item[3];\n if (!cssMapping) {\n return content;\n }\n if (typeof btoa === \"function\") {\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));\n var data = \"sourceMappingURL=data:application/json;charset=utf-8;base64,\".concat(base64);\n var sourceMapping = \"/*# \".concat(data, \" */\");\n return [content].concat([sourceMapping]).join(\"\\n\");\n }\n return [content].join(\"\\n\");\n};//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvY3NzLWxvYWRlci9kaXN0L3J1bnRpbWUvc291cmNlTWFwcy5qcyIsIm1hcHBpbmdzIjoiQUFBYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsdURBQXVELGNBQWM7QUFDckU7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvY3NzLWxvYWRlci9kaXN0L3J1bnRpbWUvc291cmNlTWFwcy5qcz9hZjEyIl0sInNvdXJjZXNDb250ZW50IjpbIlwidXNlIHN0cmljdFwiO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdGVtKSB7XG4gIHZhciBjb250ZW50ID0gaXRlbVsxXTtcbiAgdmFyIGNzc01hcHBpbmcgPSBpdGVtWzNdO1xuICBpZiAoIWNzc01hcHBpbmcpIHtcbiAgICByZXR1cm4gY29udGVudDtcbiAgfVxuICBpZiAodHlwZW9mIGJ0b2EgPT09IFwiZnVuY3Rpb25cIikge1xuICAgIHZhciBiYXNlNjQgPSBidG9hKHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudChKU09OLnN0cmluZ2lmeShjc3NNYXBwaW5nKSkpKTtcbiAgICB2YXIgZGF0YSA9IFwic291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247Y2hhcnNldD11dGYtODtiYXNlNjQsXCIuY29uY2F0KGJhc2U2NCk7XG4gICAgdmFyIHNvdXJjZU1hcHBpbmcgPSBcIi8qIyBcIi5jb25jYXQoZGF0YSwgXCIgKi9cIik7XG4gICAgcmV0dXJuIFtjb250ZW50XS5jb25jYXQoW3NvdXJjZU1hcHBpbmddKS5qb2luKFwiXFxuXCIpO1xuICB9XG4gIHJldHVybiBbY29udGVudF0uam9pbihcIlxcblwiKTtcbn07Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/css-loader/dist/runtime/sourceMaps.js\n"); - -/***/ }), - -/***/ "./resources/css/app.css": -/*!*******************************!*\ - !*** ./resources/css/app.css ***! - \*******************************/ -/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ \"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleDomAPI.js */ \"./node_modules/style-loader/dist/runtime/styleDomAPI.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertBySelector.js */ \"./node_modules/style-loader/dist/runtime/insertBySelector.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js */ \"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertStyleElement.js */ \"./node_modules/style-loader/dist/runtime/insertStyleElement.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleTagTransform.js */ \"./node_modules/style-loader/dist/runtime/styleTagTransform.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _node_modules_css_loader_dist_cjs_js_ruleSet_1_rules_1_use_1_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_app_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! !!../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./app.css */ \"./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./resources/css/app.css\");\n\n \n \n \n \n \n \n \n \n \n\nvar options = {};\n\noptions.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());\noptions.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());\n\n options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, \"head\");\n \noptions.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());\noptions.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());\n\nvar update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_ruleSet_1_rules_1_use_1_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_app_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"], options);\n\n\n\n\n /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_css_loader_dist_cjs_js_ruleSet_1_rules_1_use_1_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_app_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"] && _node_modules_css_loader_dist_cjs_js_ruleSet_1_rules_1_use_1_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_app_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals ? _node_modules_css_loader_dist_cjs_js_ruleSet_1_rules_1_use_1_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_app_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals : undefined);\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvdXJjZXMvY3NzL2FwcC5jc3MiLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7O0FBQ0EsTUFBa0c7QUFDbEcsTUFBd0Y7QUFDeEYsTUFBK0Y7QUFDL0YsTUFBa0g7QUFDbEgsTUFBMkc7QUFDM0csTUFBMkc7QUFDM0csTUFBME07QUFDMU07QUFDQTs7QUFFQTs7QUFFQSw0QkFBNEIscUdBQW1CO0FBQy9DLHdCQUF3QixrSEFBYTs7QUFFckMsdUJBQXVCLHVHQUFhO0FBQ3BDO0FBQ0EsaUJBQWlCLCtGQUFNO0FBQ3ZCLDZCQUE2QixzR0FBa0I7O0FBRS9DLGFBQWEsMEdBQUcsQ0FBQyw0S0FBTzs7OztBQUlvSjtBQUM1SyxPQUFPLCtEQUFlLDRLQUFPLElBQUksNEtBQU8sVUFBVSw0S0FBTyxtQkFBbUIsRUFBQyIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9yZXNvdXJjZXMvY3NzL2FwcC5jc3M/ODNmOCJdLCJzb3VyY2VzQ29udGVudCI6WyJcbiAgICAgIGltcG9ydCBBUEkgZnJvbSBcIiEuLi8uLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9pbmplY3RTdHlsZXNJbnRvU3R5bGVUYWcuanNcIjtcbiAgICAgIGltcG9ydCBkb21BUEkgZnJvbSBcIiEuLi8uLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9zdHlsZURvbUFQSS5qc1wiO1xuICAgICAgaW1wb3J0IGluc2VydEZuIGZyb20gXCIhLi4vLi4vbm9kZV9tb2R1bGVzL3N0eWxlLWxvYWRlci9kaXN0L3J1bnRpbWUvaW5zZXJ0QnlTZWxlY3Rvci5qc1wiO1xuICAgICAgaW1wb3J0IHNldEF0dHJpYnV0ZXMgZnJvbSBcIiEuLi8uLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9zZXRBdHRyaWJ1dGVzV2l0aG91dEF0dHJpYnV0ZXMuanNcIjtcbiAgICAgIGltcG9ydCBpbnNlcnRTdHlsZUVsZW1lbnQgZnJvbSBcIiEuLi8uLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9pbnNlcnRTdHlsZUVsZW1lbnQuanNcIjtcbiAgICAgIGltcG9ydCBzdHlsZVRhZ1RyYW5zZm9ybUZuIGZyb20gXCIhLi4vLi4vbm9kZV9tb2R1bGVzL3N0eWxlLWxvYWRlci9kaXN0L3J1bnRpbWUvc3R5bGVUYWdUcmFuc2Zvcm0uanNcIjtcbiAgICAgIGltcG9ydCBjb250ZW50LCAqIGFzIG5hbWVkRXhwb3J0IGZyb20gXCIhIS4uLy4uL25vZGVfbW9kdWxlcy9jc3MtbG9hZGVyL2Rpc3QvY2pzLmpzPz9ydWxlU2V0WzFdLnJ1bGVzWzFdLnVzZVsxXSEuLi8uLi9ub2RlX21vZHVsZXMvcG9zdGNzcy1sb2FkZXIvZGlzdC9janMuanM/P3J1bGVTZXRbMV0ucnVsZXNbMV0udXNlWzJdIS4vYXBwLmNzc1wiO1xuICAgICAgXG4gICAgICBcblxudmFyIG9wdGlvbnMgPSB7fTtcblxub3B0aW9ucy5zdHlsZVRhZ1RyYW5zZm9ybSA9IHN0eWxlVGFnVHJhbnNmb3JtRm47XG5vcHRpb25zLnNldEF0dHJpYnV0ZXMgPSBzZXRBdHRyaWJ1dGVzO1xuXG4gICAgICBvcHRpb25zLmluc2VydCA9IGluc2VydEZuLmJpbmQobnVsbCwgXCJoZWFkXCIpO1xuICAgIFxub3B0aW9ucy5kb21BUEkgPSBkb21BUEk7XG5vcHRpb25zLmluc2VydFN0eWxlRWxlbWVudCA9IGluc2VydFN0eWxlRWxlbWVudDtcblxudmFyIHVwZGF0ZSA9IEFQSShjb250ZW50LCBvcHRpb25zKTtcblxuXG5cbmV4cG9ydCAqIGZyb20gXCIhIS4uLy4uL25vZGVfbW9kdWxlcy9jc3MtbG9hZGVyL2Rpc3QvY2pzLmpzPz9ydWxlU2V0WzFdLnJ1bGVzWzFdLnVzZVsxXSEuLi8uLi9ub2RlX21vZHVsZXMvcG9zdGNzcy1sb2FkZXIvZGlzdC9janMuanM/P3J1bGVTZXRbMV0ucnVsZXNbMV0udXNlWzJdIS4vYXBwLmNzc1wiO1xuICAgICAgIGV4cG9ydCBkZWZhdWx0IGNvbnRlbnQgJiYgY29udGVudC5sb2NhbHMgPyBjb250ZW50LmxvY2FscyA6IHVuZGVmaW5lZDtcbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./resources/css/app.css\n"); - -/***/ }), - -/***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js": -/*!****************************************************************************!*\ - !*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***! - \****************************************************************************/ -/***/ (function(module) { - -eval("\n\nvar stylesInDOM = [];\nfunction getIndexByIdentifier(identifier) {\n var result = -1;\n for (var i = 0; i < stylesInDOM.length; i++) {\n if (stylesInDOM[i].identifier === identifier) {\n result = i;\n break;\n }\n }\n return result;\n}\nfunction modulesToDom(list, options) {\n var idCountMap = {};\n var identifiers = [];\n for (var i = 0; i < list.length; i++) {\n var item = list[i];\n var id = options.base ? item[0] + options.base : item[0];\n var count = idCountMap[id] || 0;\n var identifier = \"\".concat(id, \" \").concat(count);\n idCountMap[id] = count + 1;\n var indexByIdentifier = getIndexByIdentifier(identifier);\n var obj = {\n css: item[1],\n media: item[2],\n sourceMap: item[3],\n supports: item[4],\n layer: item[5]\n };\n if (indexByIdentifier !== -1) {\n stylesInDOM[indexByIdentifier].references++;\n stylesInDOM[indexByIdentifier].updater(obj);\n } else {\n var updater = addElementStyle(obj, options);\n options.byIndex = i;\n stylesInDOM.splice(i, 0, {\n identifier: identifier,\n updater: updater,\n references: 1\n });\n }\n identifiers.push(identifier);\n }\n return identifiers;\n}\nfunction addElementStyle(obj, options) {\n var api = options.domAPI(options);\n api.update(obj);\n var updater = function updater(newObj) {\n if (newObj) {\n if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {\n return;\n }\n api.update(obj = newObj);\n } else {\n api.remove();\n }\n };\n return updater;\n}\nmodule.exports = function (list, options) {\n options = options || {};\n list = list || [];\n var lastIdentifiers = modulesToDom(list, options);\n return function update(newList) {\n newList = newList || [];\n for (var i = 0; i < lastIdentifiers.length; i++) {\n var identifier = lastIdentifiers[i];\n var index = getIndexByIdentifier(identifier);\n stylesInDOM[index].references--;\n }\n var newLastIdentifiers = modulesToDom(newList, options);\n for (var _i = 0; _i < lastIdentifiers.length; _i++) {\n var _identifier = lastIdentifiers[_i];\n var _index = getIndexByIdentifier(_identifier);\n if (stylesInDOM[_index].references === 0) {\n stylesInDOM[_index].updater();\n stylesInDOM.splice(_index, 1);\n }\n }\n lastIdentifiers = newLastIdentifiers;\n };\n};//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9pbmplY3RTdHlsZXNJbnRvU3R5bGVUYWcuanMiLCJtYXBwaW5ncyI6IkFBQWE7O0FBRWI7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLHdCQUF3QjtBQUMxQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixpQkFBaUI7QUFDbkM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQiw0QkFBNEI7QUFDaEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFCQUFxQiw2QkFBNkI7QUFDbEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9qYnVrLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL3N0eWxlLWxvYWRlci9kaXN0L3J1bnRpbWUvaW5qZWN0U3R5bGVzSW50b1N0eWxlVGFnLmpzPzJkYmEiXSwic291cmNlc0NvbnRlbnQiOlsiXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBzdHlsZXNJbkRPTSA9IFtdO1xuZnVuY3Rpb24gZ2V0SW5kZXhCeUlkZW50aWZpZXIoaWRlbnRpZmllcikge1xuICB2YXIgcmVzdWx0ID0gLTE7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgc3R5bGVzSW5ET00ubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoc3R5bGVzSW5ET01baV0uaWRlbnRpZmllciA9PT0gaWRlbnRpZmllcikge1xuICAgICAgcmVzdWx0ID0gaTtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuZnVuY3Rpb24gbW9kdWxlc1RvRG9tKGxpc3QsIG9wdGlvbnMpIHtcbiAgdmFyIGlkQ291bnRNYXAgPSB7fTtcbiAgdmFyIGlkZW50aWZpZXJzID0gW107XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7IGkrKykge1xuICAgIHZhciBpdGVtID0gbGlzdFtpXTtcbiAgICB2YXIgaWQgPSBvcHRpb25zLmJhc2UgPyBpdGVtWzBdICsgb3B0aW9ucy5iYXNlIDogaXRlbVswXTtcbiAgICB2YXIgY291bnQgPSBpZENvdW50TWFwW2lkXSB8fCAwO1xuICAgIHZhciBpZGVudGlmaWVyID0gXCJcIi5jb25jYXQoaWQsIFwiIFwiKS5jb25jYXQoY291bnQpO1xuICAgIGlkQ291bnRNYXBbaWRdID0gY291bnQgKyAxO1xuICAgIHZhciBpbmRleEJ5SWRlbnRpZmllciA9IGdldEluZGV4QnlJZGVudGlmaWVyKGlkZW50aWZpZXIpO1xuICAgIHZhciBvYmogPSB7XG4gICAgICBjc3M6IGl0ZW1bMV0sXG4gICAgICBtZWRpYTogaXRlbVsyXSxcbiAgICAgIHNvdXJjZU1hcDogaXRlbVszXSxcbiAgICAgIHN1cHBvcnRzOiBpdGVtWzRdLFxuICAgICAgbGF5ZXI6IGl0ZW1bNV1cbiAgICB9O1xuICAgIGlmIChpbmRleEJ5SWRlbnRpZmllciAhPT0gLTEpIHtcbiAgICAgIHN0eWxlc0luRE9NW2luZGV4QnlJZGVudGlmaWVyXS5yZWZlcmVuY2VzKys7XG4gICAgICBzdHlsZXNJbkRPTVtpbmRleEJ5SWRlbnRpZmllcl0udXBkYXRlcihvYmopO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgdXBkYXRlciA9IGFkZEVsZW1lbnRTdHlsZShvYmosIG9wdGlvbnMpO1xuICAgICAgb3B0aW9ucy5ieUluZGV4ID0gaTtcbiAgICAgIHN0eWxlc0luRE9NLnNwbGljZShpLCAwLCB7XG4gICAgICAgIGlkZW50aWZpZXI6IGlkZW50aWZpZXIsXG4gICAgICAgIHVwZGF0ZXI6IHVwZGF0ZXIsXG4gICAgICAgIHJlZmVyZW5jZXM6IDFcbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZGVudGlmaWVycy5wdXNoKGlkZW50aWZpZXIpO1xuICB9XG4gIHJldHVybiBpZGVudGlmaWVycztcbn1cbmZ1bmN0aW9uIGFkZEVsZW1lbnRTdHlsZShvYmosIG9wdGlvbnMpIHtcbiAgdmFyIGFwaSA9IG9wdGlvbnMuZG9tQVBJKG9wdGlvbnMpO1xuICBhcGkudXBkYXRlKG9iaik7XG4gIHZhciB1cGRhdGVyID0gZnVuY3Rpb24gdXBkYXRlcihuZXdPYmopIHtcbiAgICBpZiAobmV3T2JqKSB7XG4gICAgICBpZiAobmV3T2JqLmNzcyA9PT0gb2JqLmNzcyAmJiBuZXdPYmoubWVkaWEgPT09IG9iai5tZWRpYSAmJiBuZXdPYmouc291cmNlTWFwID09PSBvYmouc291cmNlTWFwICYmIG5ld09iai5zdXBwb3J0cyA9PT0gb2JqLnN1cHBvcnRzICYmIG5ld09iai5sYXllciA9PT0gb2JqLmxheWVyKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGFwaS51cGRhdGUob2JqID0gbmV3T2JqKTtcbiAgICB9IGVsc2Uge1xuICAgICAgYXBpLnJlbW92ZSgpO1xuICAgIH1cbiAgfTtcbiAgcmV0dXJuIHVwZGF0ZXI7XG59XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChsaXN0LCBvcHRpb25zKSB7XG4gIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICBsaXN0ID0gbGlzdCB8fCBbXTtcbiAgdmFyIGxhc3RJZGVudGlmaWVycyA9IG1vZHVsZXNUb0RvbShsaXN0LCBvcHRpb25zKTtcbiAgcmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZShuZXdMaXN0KSB7XG4gICAgbmV3TGlzdCA9IG5ld0xpc3QgfHwgW107XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsYXN0SWRlbnRpZmllcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHZhciBpZGVudGlmaWVyID0gbGFzdElkZW50aWZpZXJzW2ldO1xuICAgICAgdmFyIGluZGV4ID0gZ2V0SW5kZXhCeUlkZW50aWZpZXIoaWRlbnRpZmllcik7XG4gICAgICBzdHlsZXNJbkRPTVtpbmRleF0ucmVmZXJlbmNlcy0tO1xuICAgIH1cbiAgICB2YXIgbmV3TGFzdElkZW50aWZpZXJzID0gbW9kdWxlc1RvRG9tKG5ld0xpc3QsIG9wdGlvbnMpO1xuICAgIGZvciAodmFyIF9pID0gMDsgX2kgPCBsYXN0SWRlbnRpZmllcnMubGVuZ3RoOyBfaSsrKSB7XG4gICAgICB2YXIgX2lkZW50aWZpZXIgPSBsYXN0SWRlbnRpZmllcnNbX2ldO1xuICAgICAgdmFyIF9pbmRleCA9IGdldEluZGV4QnlJZGVudGlmaWVyKF9pZGVudGlmaWVyKTtcbiAgICAgIGlmIChzdHlsZXNJbkRPTVtfaW5kZXhdLnJlZmVyZW5jZXMgPT09IDApIHtcbiAgICAgICAgc3R5bGVzSW5ET01bX2luZGV4XS51cGRhdGVyKCk7XG4gICAgICAgIHN0eWxlc0luRE9NLnNwbGljZShfaW5kZXgsIDEpO1xuICAgICAgfVxuICAgIH1cbiAgICBsYXN0SWRlbnRpZmllcnMgPSBuZXdMYXN0SWRlbnRpZmllcnM7XG4gIH07XG59OyJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\n"); - -/***/ }), - -/***/ "./node_modules/style-loader/dist/runtime/insertBySelector.js": -/*!********************************************************************!*\ - !*** ./node_modules/style-loader/dist/runtime/insertBySelector.js ***! - \********************************************************************/ -/***/ (function(module) { - -eval("\n\nvar memo = {};\n\n/* istanbul ignore next */\nfunction getTarget(target) {\n if (typeof memo[target] === \"undefined\") {\n var styleTarget = document.querySelector(target);\n\n // Special case to return head of iframe instead of iframe itself\n if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n try {\n // This will throw an exception if access to iframe is blocked\n // due to cross-origin restrictions\n styleTarget = styleTarget.contentDocument.head;\n } catch (e) {\n // istanbul ignore next\n styleTarget = null;\n }\n }\n memo[target] = styleTarget;\n }\n return memo[target];\n}\n\n/* istanbul ignore next */\nfunction insertBySelector(insert, style) {\n var target = getTarget(insert);\n if (!target) {\n throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");\n }\n target.appendChild(style);\n}\nmodule.exports = insertBySelector;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9pbnNlcnRCeVNlbGVjdG9yLmpzIiwibWFwcGluZ3MiOiJBQUFhOztBQUViOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9pbnNlcnRCeVNlbGVjdG9yLmpzP2IyMTQiXSwic291cmNlc0NvbnRlbnQiOlsiXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBtZW1vID0ge307XG5cbi8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICAqL1xuZnVuY3Rpb24gZ2V0VGFyZ2V0KHRhcmdldCkge1xuICBpZiAodHlwZW9mIG1lbW9bdGFyZ2V0XSA9PT0gXCJ1bmRlZmluZWRcIikge1xuICAgIHZhciBzdHlsZVRhcmdldCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IodGFyZ2V0KTtcblxuICAgIC8vIFNwZWNpYWwgY2FzZSB0byByZXR1cm4gaGVhZCBvZiBpZnJhbWUgaW5zdGVhZCBvZiBpZnJhbWUgaXRzZWxmXG4gICAgaWYgKHdpbmRvdy5IVE1MSUZyYW1lRWxlbWVudCAmJiBzdHlsZVRhcmdldCBpbnN0YW5jZW9mIHdpbmRvdy5IVE1MSUZyYW1lRWxlbWVudCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gVGhpcyB3aWxsIHRocm93IGFuIGV4Y2VwdGlvbiBpZiBhY2Nlc3MgdG8gaWZyYW1lIGlzIGJsb2NrZWRcbiAgICAgICAgLy8gZHVlIHRvIGNyb3NzLW9yaWdpbiByZXN0cmljdGlvbnNcbiAgICAgICAgc3R5bGVUYXJnZXQgPSBzdHlsZVRhcmdldC5jb250ZW50RG9jdW1lbnQuaGVhZDtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8gaXN0YW5idWwgaWdub3JlIG5leHRcbiAgICAgICAgc3R5bGVUYXJnZXQgPSBudWxsO1xuICAgICAgfVxuICAgIH1cbiAgICBtZW1vW3RhcmdldF0gPSBzdHlsZVRhcmdldDtcbiAgfVxuICByZXR1cm4gbWVtb1t0YXJnZXRdO1xufVxuXG4vKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAgKi9cbmZ1bmN0aW9uIGluc2VydEJ5U2VsZWN0b3IoaW5zZXJ0LCBzdHlsZSkge1xuICB2YXIgdGFyZ2V0ID0gZ2V0VGFyZ2V0KGluc2VydCk7XG4gIGlmICghdGFyZ2V0KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiQ291bGRuJ3QgZmluZCBhIHN0eWxlIHRhcmdldC4gVGhpcyBwcm9iYWJseSBtZWFucyB0aGF0IHRoZSB2YWx1ZSBmb3IgdGhlICdpbnNlcnQnIHBhcmFtZXRlciBpcyBpbnZhbGlkLlwiKTtcbiAgfVxuICB0YXJnZXQuYXBwZW5kQ2hpbGQoc3R5bGUpO1xufVxubW9kdWxlLmV4cG9ydHMgPSBpbnNlcnRCeVNlbGVjdG9yOyJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/style-loader/dist/runtime/insertBySelector.js\n"); - -/***/ }), - -/***/ "./node_modules/style-loader/dist/runtime/insertStyleElement.js": -/*!**********************************************************************!*\ - !*** ./node_modules/style-loader/dist/runtime/insertStyleElement.js ***! - \**********************************************************************/ -/***/ (function(module) { - -eval("\n\n/* istanbul ignore next */\nfunction insertStyleElement(options) {\n var element = document.createElement(\"style\");\n options.setAttributes(element, options.attributes);\n options.insert(element, options.options);\n return element;\n}\nmodule.exports = insertStyleElement;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9pbnNlcnRTdHlsZUVsZW1lbnQuanMiLCJtYXBwaW5ncyI6IkFBQWE7O0FBRWI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9pbnNlcnRTdHlsZUVsZW1lbnQuanM/ZGU2YyJdLCJzb3VyY2VzQ29udGVudCI6WyJcInVzZSBzdHJpY3RcIjtcblxuLyogaXN0YW5idWwgaWdub3JlIG5leHQgICovXG5mdW5jdGlvbiBpbnNlcnRTdHlsZUVsZW1lbnQob3B0aW9ucykge1xuICB2YXIgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzdHlsZVwiKTtcbiAgb3B0aW9ucy5zZXRBdHRyaWJ1dGVzKGVsZW1lbnQsIG9wdGlvbnMuYXR0cmlidXRlcyk7XG4gIG9wdGlvbnMuaW5zZXJ0KGVsZW1lbnQsIG9wdGlvbnMub3B0aW9ucyk7XG4gIHJldHVybiBlbGVtZW50O1xufVxubW9kdWxlLmV4cG9ydHMgPSBpbnNlcnRTdHlsZUVsZW1lbnQ7Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/style-loader/dist/runtime/insertStyleElement.js\n"); - -/***/ }), - -/***/ "./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js": -/*!**********************************************************************************!*\ - !*** ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js ***! - \**********************************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -eval("\n\n/* istanbul ignore next */\nfunction setAttributesWithoutAttributes(styleElement) {\n var nonce = true ? __webpack_require__.nc : 0;\n if (nonce) {\n styleElement.setAttribute(\"nonce\", nonce);\n }\n}\nmodule.exports = setAttributesWithoutAttributes;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9zZXRBdHRyaWJ1dGVzV2l0aG91dEF0dHJpYnV0ZXMuanMiLCJtYXBwaW5ncyI6IkFBQWE7O0FBRWI7QUFDQTtBQUNBLGNBQWMsS0FBd0MsR0FBRyxzQkFBaUIsR0FBRyxDQUFJO0FBQ2pGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9qYnVrLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL3N0eWxlLWxvYWRlci9kaXN0L3J1bnRpbWUvc2V0QXR0cmlidXRlc1dpdGhvdXRBdHRyaWJ1dGVzLmpzP2RkY2UiXSwic291cmNlc0NvbnRlbnQiOlsiXCJ1c2Ugc3RyaWN0XCI7XG5cbi8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICAqL1xuZnVuY3Rpb24gc2V0QXR0cmlidXRlc1dpdGhvdXRBdHRyaWJ1dGVzKHN0eWxlRWxlbWVudCkge1xuICB2YXIgbm9uY2UgPSB0eXBlb2YgX193ZWJwYWNrX25vbmNlX18gIT09IFwidW5kZWZpbmVkXCIgPyBfX3dlYnBhY2tfbm9uY2VfXyA6IG51bGw7XG4gIGlmIChub25jZSkge1xuICAgIHN0eWxlRWxlbWVudC5zZXRBdHRyaWJ1dGUoXCJub25jZVwiLCBub25jZSk7XG4gIH1cbn1cbm1vZHVsZS5leHBvcnRzID0gc2V0QXR0cmlidXRlc1dpdGhvdXRBdHRyaWJ1dGVzOyJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\n"); - -/***/ }), - -/***/ "./node_modules/style-loader/dist/runtime/styleDomAPI.js": -/*!***************************************************************!*\ - !*** ./node_modules/style-loader/dist/runtime/styleDomAPI.js ***! - \***************************************************************/ -/***/ (function(module) { - -eval("\n\n/* istanbul ignore next */\nfunction apply(styleElement, options, obj) {\n var css = \"\";\n if (obj.supports) {\n css += \"@supports (\".concat(obj.supports, \") {\");\n }\n if (obj.media) {\n css += \"@media \".concat(obj.media, \" {\");\n }\n var needLayer = typeof obj.layer !== \"undefined\";\n if (needLayer) {\n css += \"@layer\".concat(obj.layer.length > 0 ? \" \".concat(obj.layer) : \"\", \" {\");\n }\n css += obj.css;\n if (needLayer) {\n css += \"}\";\n }\n if (obj.media) {\n css += \"}\";\n }\n if (obj.supports) {\n css += \"}\";\n }\n var sourceMap = obj.sourceMap;\n if (sourceMap && typeof btoa !== \"undefined\") {\n css += \"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), \" */\");\n }\n\n // For old IE\n /* istanbul ignore if */\n options.styleTagTransform(css, styleElement, options.options);\n}\nfunction removeStyleElement(styleElement) {\n // istanbul ignore if\n if (styleElement.parentNode === null) {\n return false;\n }\n styleElement.parentNode.removeChild(styleElement);\n}\n\n/* istanbul ignore next */\nfunction domAPI(options) {\n if (typeof document === \"undefined\") {\n return {\n update: function update() {},\n remove: function remove() {}\n };\n }\n var styleElement = options.insertStyleElement(options);\n return {\n update: function update(obj) {\n apply(styleElement, options, obj);\n },\n remove: function remove() {\n removeStyleElement(styleElement);\n }\n };\n}\nmodule.exports = domAPI;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9zdHlsZURvbUFQSS5qcyIsIm1hcHBpbmdzIjoiQUFBYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtEQUFrRDtBQUNsRDtBQUNBO0FBQ0EsMENBQTBDO0FBQzFDO0FBQ0E7QUFDQTtBQUNBLGlGQUFpRjtBQUNqRjtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBLHlEQUF5RDtBQUN6RDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0NBQWtDO0FBQ2xDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9zdHlsZURvbUFQSS5qcz9lNDc5Il0sInNvdXJjZXNDb250ZW50IjpbIlwidXNlIHN0cmljdFwiO1xuXG4vKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAgKi9cbmZ1bmN0aW9uIGFwcGx5KHN0eWxlRWxlbWVudCwgb3B0aW9ucywgb2JqKSB7XG4gIHZhciBjc3MgPSBcIlwiO1xuICBpZiAob2JqLnN1cHBvcnRzKSB7XG4gICAgY3NzICs9IFwiQHN1cHBvcnRzIChcIi5jb25jYXQob2JqLnN1cHBvcnRzLCBcIikge1wiKTtcbiAgfVxuICBpZiAob2JqLm1lZGlhKSB7XG4gICAgY3NzICs9IFwiQG1lZGlhIFwiLmNvbmNhdChvYmoubWVkaWEsIFwiIHtcIik7XG4gIH1cbiAgdmFyIG5lZWRMYXllciA9IHR5cGVvZiBvYmoubGF5ZXIgIT09IFwidW5kZWZpbmVkXCI7XG4gIGlmIChuZWVkTGF5ZXIpIHtcbiAgICBjc3MgKz0gXCJAbGF5ZXJcIi5jb25jYXQob2JqLmxheWVyLmxlbmd0aCA+IDAgPyBcIiBcIi5jb25jYXQob2JqLmxheWVyKSA6IFwiXCIsIFwiIHtcIik7XG4gIH1cbiAgY3NzICs9IG9iai5jc3M7XG4gIGlmIChuZWVkTGF5ZXIpIHtcbiAgICBjc3MgKz0gXCJ9XCI7XG4gIH1cbiAgaWYgKG9iai5tZWRpYSkge1xuICAgIGNzcyArPSBcIn1cIjtcbiAgfVxuICBpZiAob2JqLnN1cHBvcnRzKSB7XG4gICAgY3NzICs9IFwifVwiO1xuICB9XG4gIHZhciBzb3VyY2VNYXAgPSBvYmouc291cmNlTWFwO1xuICBpZiAoc291cmNlTWFwICYmIHR5cGVvZiBidG9hICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgY3NzICs9IFwiXFxuLyojIHNvdXJjZU1hcHBpbmdVUkw9ZGF0YTphcHBsaWNhdGlvbi9qc29uO2Jhc2U2NCxcIi5jb25jYXQoYnRvYSh1bmVzY2FwZShlbmNvZGVVUklDb21wb25lbnQoSlNPTi5zdHJpbmdpZnkoc291cmNlTWFwKSkpKSwgXCIgKi9cIik7XG4gIH1cblxuICAvLyBGb3Igb2xkIElFXG4gIC8qIGlzdGFuYnVsIGlnbm9yZSBpZiAgKi9cbiAgb3B0aW9ucy5zdHlsZVRhZ1RyYW5zZm9ybShjc3MsIHN0eWxlRWxlbWVudCwgb3B0aW9ucy5vcHRpb25zKTtcbn1cbmZ1bmN0aW9uIHJlbW92ZVN0eWxlRWxlbWVudChzdHlsZUVsZW1lbnQpIHtcbiAgLy8gaXN0YW5idWwgaWdub3JlIGlmXG4gIGlmIChzdHlsZUVsZW1lbnQucGFyZW50Tm9kZSA9PT0gbnVsbCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBzdHlsZUVsZW1lbnQucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChzdHlsZUVsZW1lbnQpO1xufVxuXG4vKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAgKi9cbmZ1bmN0aW9uIGRvbUFQSShvcHRpb25zKSB7XG4gIGlmICh0eXBlb2YgZG9jdW1lbnQgPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICByZXR1cm4ge1xuICAgICAgdXBkYXRlOiBmdW5jdGlvbiB1cGRhdGUoKSB7fSxcbiAgICAgIHJlbW92ZTogZnVuY3Rpb24gcmVtb3ZlKCkge31cbiAgICB9O1xuICB9XG4gIHZhciBzdHlsZUVsZW1lbnQgPSBvcHRpb25zLmluc2VydFN0eWxlRWxlbWVudChvcHRpb25zKTtcbiAgcmV0dXJuIHtcbiAgICB1cGRhdGU6IGZ1bmN0aW9uIHVwZGF0ZShvYmopIHtcbiAgICAgIGFwcGx5KHN0eWxlRWxlbWVudCwgb3B0aW9ucywgb2JqKTtcbiAgICB9LFxuICAgIHJlbW92ZTogZnVuY3Rpb24gcmVtb3ZlKCkge1xuICAgICAgcmVtb3ZlU3R5bGVFbGVtZW50KHN0eWxlRWxlbWVudCk7XG4gICAgfVxuICB9O1xufVxubW9kdWxlLmV4cG9ydHMgPSBkb21BUEk7Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/style-loader/dist/runtime/styleDomAPI.js\n"); - -/***/ }), - -/***/ "./node_modules/style-loader/dist/runtime/styleTagTransform.js": -/*!*********************************************************************!*\ - !*** ./node_modules/style-loader/dist/runtime/styleTagTransform.js ***! - \*********************************************************************/ -/***/ (function(module) { - -eval("\n\n/* istanbul ignore next */\nfunction styleTagTransform(css, styleElement) {\n if (styleElement.styleSheet) {\n styleElement.styleSheet.cssText = css;\n } else {\n while (styleElement.firstChild) {\n styleElement.removeChild(styleElement.firstChild);\n }\n styleElement.appendChild(document.createTextNode(css));\n }\n}\nmodule.exports = styleTagTransform;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9zdHlsZVRhZ1RyYW5zZm9ybS5qcyIsIm1hcHBpbmdzIjoiQUFBYTs7QUFFYjtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZXMiOlsid2VicGFjazovL2pidWstZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvc3R5bGUtbG9hZGVyL2Rpc3QvcnVudGltZS9zdHlsZVRhZ1RyYW5zZm9ybS5qcz8xZGRlIl0sInNvdXJjZXNDb250ZW50IjpbIlwidXNlIHN0cmljdFwiO1xuXG4vKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAgKi9cbmZ1bmN0aW9uIHN0eWxlVGFnVHJhbnNmb3JtKGNzcywgc3R5bGVFbGVtZW50KSB7XG4gIGlmIChzdHlsZUVsZW1lbnQuc3R5bGVTaGVldCkge1xuICAgIHN0eWxlRWxlbWVudC5zdHlsZVNoZWV0LmNzc1RleHQgPSBjc3M7XG4gIH0gZWxzZSB7XG4gICAgd2hpbGUgKHN0eWxlRWxlbWVudC5maXJzdENoaWxkKSB7XG4gICAgICBzdHlsZUVsZW1lbnQucmVtb3ZlQ2hpbGQoc3R5bGVFbGVtZW50LmZpcnN0Q2hpbGQpO1xuICAgIH1cbiAgICBzdHlsZUVsZW1lbnQuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoY3NzKSk7XG4gIH1cbn1cbm1vZHVsZS5leHBvcnRzID0gc3R5bGVUYWdUcmFuc2Zvcm07Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/style-loader/dist/runtime/styleTagTransform.js\n"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ id: moduleId, -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat get default export */ -/******/ !function() { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function() { return module['default']; } : -/******/ function() { return module; }; -/******/ __webpack_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ }(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ !function() { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = function(exports, definition) { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ }(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ !function() { -/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } -/******/ }(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ !function() { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ }(); -/******/ -/******/ /* webpack/runtime/nonce */ -/******/ !function() { -/******/ __webpack_require__.nc = undefined; -/******/ }(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval-source-map devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./resources/js/app.js"); -/******/ -/******/ })() -; \ No newline at end of file +!function(){"use strict";let e=new class{constructor(){}async register(){const e=await this.getCreateOptions(),t={challenge:this.base64URLStringToBuffer(e.challenge),rp:{id:e.rp.id,name:e.rp.name},user:{id:(new TextEncoder).encode(window.atob(e.user.id)),name:e.user.name,displayName:e.user.displayName},pubKeyCredParams:e.pubKeyCredParams,excludeCredentials:[],authenticatorSelection:e.authenticatorSelection,timeout:6e4},a=await navigator.credentials.create({publicKey:t});if(!a)throw new Error("Error generating a passkey");const n={id:a.id?a.id:null,type:a.type?a.type:null,rawId:a.rawId?this.bufferToBase64URLString(a.rawId):null,response:{attestationObject:a.response.attestationObject?this.bufferToBase64URLString(a.response.attestationObject):null,clientDataJSON:a.response.clientDataJSON?this.bufferToBase64URLString(a.response.clientDataJSON):null}};if(!(await window.fetch("/admin/passkeys/register",{method:"POST",body:JSON.stringify(n),cache:"no-cache",headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Error saving the passkey");window.location.reload()}async getCreateOptions(){const e=await fetch("/admin/passkeys/register",{method:"GET"});return await e.json()}async login(){const e=await this.getLoginData(),t=await navigator.credentials.get({publicKey:{challenge:this.base64URLStringToBuffer(e.challenge),userVerification:e.userVerification,timeout:6e4}});if(!t)throw new Error("Authentication failed");const a={id:t.id?t.id:"",type:t.type?t.type:"",rawId:t.rawId?this.bufferToBase64URLString(t.rawId):"",response:{authenticatorData:t.response.authenticatorData?this.bufferToBase64URLString(t.response.authenticatorData):"",clientDataJSON:t.response.clientDataJSON?this.bufferToBase64URLString(t.response.clientDataJSON):"",signature:t.response.signature?this.bufferToBase64URLString(t.response.signature):"",userHandle:t.response.userHandle?this.bufferToBase64URLString(t.response.userHandle):""}};if(!(await window.fetch("/login/passkey",{method:"POST",body:JSON.stringify(a),headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Login failed");window.location.assign("/admin")}async getLoginData(){const e=await fetch("/login/passkey",{method:"GET"});return await e.json()}base64URLStringToBuffer(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),a=(4-t.length%4)%4,n=t.padEnd(t.length+a,"="),r=window.atob(n),i=new ArrayBuffer(r.length),s=new Uint8Array(i);for(let e=0;e{t.addEventListener("click",(()=>{e.register()}))})),document.querySelectorAll(".login-passkey").forEach((t=>{t.addEventListener("click",(()=>{e.login()}))}))}(); \ No newline at end of file diff --git a/public/assets/app.js.br b/public/assets/app.js.br index ed35c5c4..bbaa2f49 100644 Binary files a/public/assets/app.js.br and b/public/assets/app.js.br differ diff --git a/resources/css/colours.css b/resources/css/colours.css index 65b95212..0374fbe8 100644 --- a/resources/css/colours.css +++ b/resources/css/colours.css @@ -9,6 +9,10 @@ a { &:visited { color: var(--color-link-visited); } + + &.auth:visited { + color: var(--color-link); + } } #site-header { diff --git a/resources/css/layout.css b/resources/css/layout.css index f0a40dd6..18cb9448 100644 --- a/resources/css/layout.css +++ b/resources/css/layout.css @@ -22,4 +22,10 @@ footer { & .iwc-logo { max-width: 85vw; } + + & .footer-actions { + display: flex; + flex-direction: row; + gap: 1rem; + } } diff --git a/resources/js/app.js b/resources/js/app.js index 06c635a9..7411e06e 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -9,3 +9,9 @@ document.querySelectorAll('.add-passkey').forEach((el) => { auth.register(); }); }); + +document.querySelectorAll('.login-passkey').forEach((el) => { + el.addEventListener('click', () => { + auth.login(); + }); +}); diff --git a/resources/js/auth.js b/resources/js/auth.js index 905db945..15d71ac1 100644 --- a/resources/js/auth.js +++ b/resources/js/auth.js @@ -2,87 +2,165 @@ class Auth { constructor() {} async register() { - const { challenge, userId, existing } = await this.getRegisterData(); + const createOptions = await this.getCreateOptions(); const publicKeyCredentialCreationOptions = { - challenge: new TextEncoder().encode(challenge), + challenge: this.base64URLStringToBuffer(createOptions.challenge), rp: { - name: 'JB', + id: createOptions.rp.id, + name: createOptions.rp.name, }, user: { - id: new TextEncoder().encode(userId), - name: 'jonny@jonnybarnes.uk', - displayName: 'Jonny', - }, - pubKeyCredParams: [ - {alg: -8, type: 'public-key'}, // Ed25519 - {alg: -7, type: 'public-key'}, // ES256 - {alg: -257, type: 'public-key'}, // RS256 - ], - excludeCredentials: existing, - authenticatorSelection: { - userVerification: 'preferred', - residentKey: 'required', + id: new TextEncoder().encode(window.atob(createOptions.user.id)), + name: createOptions.user.name, + displayName: createOptions.user.displayName, }, + pubKeyCredParams: createOptions.pubKeyCredParams, + excludeCredentials: [], + authenticatorSelection: createOptions.authenticatorSelection, timeout: 60000, }; - const publicKeyCredential = await navigator.credentials.create({ + const credential = await navigator.credentials.create({ publicKey: publicKeyCredentialCreationOptions }); - if (!publicKeyCredential) { + if (!credential) { throw new Error('Error generating a passkey'); } - const { - id // the key id a.k.a. kid - } = publicKeyCredential; - const publicKey = publicKeyCredential.response.getPublicKey(); - const transports = publicKeyCredential.response.getTransports(); - const response = publicKeyCredential.response; - const clientJSONArrayBuffer = response.clientDataJSON; - const clientJSON = JSON.parse(new TextDecoder().decode(clientJSONArrayBuffer)); - const clientChallenge = clientJSON.challenge; - // base64 decode the challenge - const clientChallengeDecoded = atob(clientChallenge); - const saved = await this.savePasskey(id, publicKey, transports, clientChallengeDecoded); + const authenticatorAttestationResponse = { + id: credential.id ? credential.id : null, + type: credential.type ? credential.type : null, + rawId: credential.rawId ? this.bufferToBase64URLString(credential.rawId) : null, + response: { + attestationObject: credential.response.attestationObject ? this.bufferToBase64URLString(credential.response.attestationObject) : null, + clientDataJSON: credential.response.clientDataJSON ? this.bufferToBase64URLString(credential.response.clientDataJSON) : null, + } + }; - if (saved) { - window.location.reload(); - } else { - alert('There was an error saving the passkey'); - } - } - - async getRegisterData() { - const response = await fetch('/admin/passkeys/init'); - - return await response.json(); - } - - async savePasskey(id, publicKey, transports, challenge) { - const formData = new FormData(); - formData.append('id', id); - formData.append('transports', JSON.stringify(transports)); - formData.append('challenge', challenge); - - // Convert the ArrayBuffer to a Uint8Array - const publicKeyArray = new Uint8Array(publicKey); - - // Create a Blob from the Uint8Array - const publicKeyBlob = new Blob([publicKeyArray], { type: 'application/octet-stream' }); - - formData.append('public_key', publicKeyBlob); - - const response = await fetch('/admin/passkeys/save', { + const registerCredential = await window.fetch('/admin/passkeys/register', { method: 'POST', - body: formData, + body: JSON.stringify(authenticatorAttestationResponse), + cache: 'no-cache', headers: { + 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), }, }); - return response.ok; + if (!registerCredential.ok) { + throw new Error('Error saving the passkey'); + } + + window.location.reload(); + } + + async getCreateOptions() { + const response = await fetch('/admin/passkeys/register', { + method: 'GET', + }); + + return await response.json(); + } + + async login() { + const loginData = await this.getLoginData(); + + const publicKeyCredential = await navigator.credentials.get({ + publicKey: { + challenge: this.base64URLStringToBuffer(loginData.challenge), + userVerification: loginData.userVerification, + timeout: 60000, + } + }); + + if (!publicKeyCredential) { + throw new Error('Authentication failed'); + } + + const authenticatorAttestationResponse = { + id: publicKeyCredential.id ? publicKeyCredential.id : '', + type: publicKeyCredential.type ? publicKeyCredential.type : '', + rawId: publicKeyCredential.rawId ? this.bufferToBase64URLString(publicKeyCredential.rawId) : '', + response: { + authenticatorData: publicKeyCredential.response.authenticatorData ? this.bufferToBase64URLString(publicKeyCredential.response.authenticatorData) : '', + clientDataJSON: publicKeyCredential.response.clientDataJSON ? this.bufferToBase64URLString(publicKeyCredential.response.clientDataJSON) : '', + signature: publicKeyCredential.response.signature ? this.bufferToBase64URLString(publicKeyCredential.response.signature) : '', + userHandle: publicKeyCredential.response.userHandle ? this.bufferToBase64URLString(publicKeyCredential.response.userHandle) : '', + }, + }; + + const loginAttempt = await window.fetch('/login/passkey', { + method: 'POST', + body: JSON.stringify(authenticatorAttestationResponse), + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), + }, + }); + + if (!loginAttempt.ok) { + throw new Error('Login failed'); + } + + window.location.assign('/admin'); + } + + async getLoginData() { + const response = await fetch('/login/passkey', { + method: 'GET', + }); + + return await response.json(); + } + + /** + * Convert a base64 URL string to a buffer. + * + * Sourced from https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/browser/src/helpers/base64URLStringToBuffer.ts#L8 + * + * @param {string} base64URLString + * @returns {ArrayBuffer} + */ + base64URLStringToBuffer(base64URLString) { + // Convert from Base64URL to Base64 + const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/'); + /** + * Pad with '=' until it's a multiple of four + * (4 - (85 % 4 = 1) = 3) % 4 = 3 padding + * (4 - (86 % 4 = 2) = 2) % 4 = 2 padding + * (4 - (87 % 4 = 3) = 1) % 4 = 1 padding + * (4 - (88 % 4 = 0) = 4) % 4 = 0 padding + */ + const padLength = (4 - (base64.length % 4)) % 4; + const padded = base64.padEnd(base64.length + padLength, '='); + // Convert to a binary string + const binary = window.atob(padded); + // Convert binary string to buffer + const buffer = new ArrayBuffer(binary.length); + const bytes = new Uint8Array(buffer); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return buffer; + } + + /** + * Convert a buffer to a base64 URL string. + * + * Sourced from https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/browser/src/helpers/bufferToBase64URLString.ts#L7 + * + * @param {ArrayBuffer} buffer + * @returns {string} + */ + bufferToBase64URLString(buffer) { + const bytes = new Uint8Array(buffer); + let str = ''; + for (const charCode of bytes) { + str += String.fromCharCode(charCode); + } + const base64String = btoa(str); + return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); } } diff --git a/resources/views/login.blade.php b/resources/views/login.blade.php index e3c3839b..b80bd147 100644 --- a/resources/views/login.blade.php +++ b/resources/views/login.blade.php @@ -9,4 +9,5 @@ +

@stop diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php index 24eb5665..d983821e 100644 --- a/resources/views/master.blade.php +++ b/resources/views/master.blade.php @@ -54,9 +54,16 @@ diff --git a/routes/web.php b/routes/web.php index 18a17619..fbc6e129 100644 --- a/routes/web.php +++ b/routes/web.php @@ -50,6 +50,8 @@ Route::group(['domain' => config('url.longurl')], function () { // The login routes to get auth’d for admin Route::get('login', [AuthController::class, 'showLogin'])->name('login'); Route::post('login', [AuthController::class, 'login']); + Route::get('login/passkey', [PasskeysController::class, 'getRequestOptions']); + Route::post('login/passkey', [PasskeysController::class, 'login']); // And the logout routes Route::get('logout', [AuthController::class, 'showLogout'])->name('logout'); @@ -146,8 +148,8 @@ Route::group(['domain' => config('url.longurl')], function () { // Passkeys Route::group(['prefix' => 'passkeys'], static function () { Route::get('/', [PasskeysController::class, 'index']); - Route::post('save', [PasskeysController::class, 'save']); - Route::get('/init', [PasskeysController::class, 'init']); + Route::get('register', [PasskeysController::class, 'getCreateOptions']); + Route::post('register', [PasskeysController::class, 'create']); }); }); diff --git a/tests/Feature/Admin/AdminTest.php b/tests/Feature/Admin/AdminTest.php index 77c0ab08..d69d59a4 100644 --- a/tests/Feature/Admin/AdminTest.php +++ b/tests/Feature/Admin/AdminTest.php @@ -46,7 +46,7 @@ class AdminTest extends TestCase 'password' => 'password', ]); - $response->assertRedirect('/'); + $response->assertRedirect('/admin'); } /** @test */