diff --git a/config.ini.example b/config.ini.example
index 18f874d8319afa3ba56ec7d2c7c090f55350b871..25c10e8d87ecf6379c13386095c4e5027814ed43 100644
--- a/config.ini.example
+++ b/config.ini.example
@@ -1,6 +1,6 @@
 [app]
 env = development
-esbuild_metafile = %DM_ROOT%/esbuild-meta-app.json
+esbuild_metafile = null
 scheme = https
 url = http://localhost:9501
 
@@ -79,8 +79,8 @@ sitemap = %DM_ROOT%/docs/build/sitemap.xml
 host = 127.0.0.1
 port = 9501
 log_level = SWOOLE_LOG_DEBUG
-ssl_cert_file = %DM_ROOT%/ssl/origin.crt
-ssl_key_file = %DM_ROOT%/ssl/origin.key
+ssl_cert_file = null
+ssl_key_file = null
 
 [translator]
 base_directory = %DM_APP_ROOT%/lang
diff --git a/docs/pages/docs/changelog/index.md b/docs/pages/docs/changelog/index.md
index 5bb6603cdf168187d571bf0cc2ae2e655f02549d..93dbe5c36aa5a5069904f9160449459098ad31d2 100644
--- a/docs/pages/docs/changelog/index.md
+++ b/docs/pages/docs/changelog/index.md
@@ -10,6 +10,10 @@ title: Changelog
 
 # Changelog
 
+## v0.23.0
+
+- Feature: filename constraint in {{docs/features/validation/constraints/index}}
+
 ## v0.22.0
 
 - Change: switch to absolute paths in {{docs/features/configuration/index}}
diff --git a/docs/pages/docs/features/validation/constraints/index.md b/docs/pages/docs/features/validation/constraints/index.md
index f80cb8401c55aa98835ecf7b024cbfa724d423ff..62eddd57821134b88b42e74f8887bd1028cd5540 100644
--- a/docs/pages/docs/features/validation/constraints/index.md
+++ b/docs/pages/docs/features/validation/constraints/index.md
@@ -78,7 +78,7 @@ new ConstConstraint(constValue: $constValue);
 }
 ```
 
-Accepts exactly the provided value
+Accepts exactly the provided value.
 
 ### Enum
 
@@ -95,6 +95,20 @@ new EnumConstraint(values: $values);
 }
 ```
 
+### Filename
+
+Checks if a given file exists and is readable.
+
+```php
+new FilenameConstraint();
+```
+```json
+{ 
+    "type": "string",
+    "enum": [...]
+}
+```
+
 ### Integer
 
 ```php
diff --git a/src/ApplicationConfiguration.php b/src/ApplicationConfiguration.php
index aa9b8308ef943081bd08dd10f132372bd118b946..4eb016ab8cf8fff0b31b0e9c07226054bb4874f4 100644
--- a/src/ApplicationConfiguration.php
+++ b/src/ApplicationConfiguration.php
@@ -13,7 +13,7 @@ readonly class ApplicationConfiguration
      */
     public function __construct(
         public Environment $environment,
-        public string $esbuildMetafile,
+        public ?string $esbuildMetafile,
         public string $scheme,
         public string $url,
     ) {}
diff --git a/src/Command/Watch.php b/src/Command/Watch.php
index ab17e8c91a55ad2163da387111cb426e2cdc497d..a31d1058d9af9bd2f42ff34f5b2893be4f9e4b76 100644
--- a/src/Command/Watch.php
+++ b/src/Command/Watch.php
@@ -43,16 +43,19 @@ final class Watch extends Command
          */
         $childCommandName = $input->getArgument('name');
 
-        $directories = [
+        $files = [
             DM_APP_ROOT,
             DM_APP_ROOT.'/../config.ini',
-            $this->applicationConfiguration->esbuildMetafile,
             DM_RESONANCE_ROOT,
         ];
 
+        if (is_string($this->applicationConfiguration->esbuildMetafile)) {
+            $files[] = $this->applicationConfiguration->esbuildMetafile;
+        }
+
         $this->restartChildCommand($childCommandName);
 
-        foreach (new InotifyIterator($directories) as $event) {
+        foreach (new InotifyIterator($files) as $event) {
             $this->logger->info(sprintf('watch_file_changed(%s)', $event['name']));
 
             $this->restartChildCommand($childCommandName);
diff --git a/src/Constraint/FilenameConstraint.php b/src/Constraint/FilenameConstraint.php
new file mode 100644
index 0000000000000000000000000000000000000000..12e6937b48a2d8ef281d4ecdea831e3fb118c982
--- /dev/null
+++ b/src/Constraint/FilenameConstraint.php
@@ -0,0 +1,99 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Distantmagic\Resonance\Constraint;
+
+use Distantmagic\Resonance\Constraint;
+use Distantmagic\Resonance\ConstraintDefaultValue;
+use Distantmagic\Resonance\ConstraintPath;
+use Distantmagic\Resonance\ConstraintReason;
+use Distantmagic\Resonance\ConstraintResult;
+use Distantmagic\Resonance\ConstraintResultStatus;
+
+final readonly class FilenameConstraint extends Constraint
+{
+    public function __construct(
+        ?ConstraintDefaultValue $defaultValue = null,
+        bool $isNullable = false,
+        bool $isRequired = true,
+    ) {
+        parent::__construct(
+            defaultValue: $defaultValue,
+            isNullable: $isNullable,
+            isRequired: $isRequired,
+        );
+    }
+
+    public function default(mixed $defaultValue): self
+    {
+        return new self(
+            defaultValue: new ConstraintDefaultValue($defaultValue),
+            isNullable: $this->isNullable,
+            isRequired: $this->isRequired,
+        );
+    }
+
+    public function nullable(): self
+    {
+        return new self(
+            defaultValue: $this->defaultValue ?? new ConstraintDefaultValue(null),
+            isNullable: true,
+            isRequired: $this->isRequired,
+        );
+    }
+
+    public function optional(): self
+    {
+        return new self(
+            defaultValue: $this->defaultValue,
+            isNullable: $this->isNullable,
+            isRequired: false,
+        );
+    }
+
+    protected function doConvertToJsonSchema(): array
+    {
+        return [
+            'type' => 'string',
+            'minLength' => 1,
+        ];
+    }
+
+    protected function doValidate(mixed $notValidatedData, ConstraintPath $path): ConstraintResult
+    {
+        if (!is_string($notValidatedData) || empty($notValidatedData)) {
+            return new ConstraintResult(
+                castedData: $notValidatedData,
+                path: $path,
+                reason: ConstraintReason::InvalidDataType,
+                status: ConstraintResultStatus::Invalid,
+            );
+        }
+
+        if (!file_exists($notValidatedData)) {
+            return new ConstraintResult(
+                castedData: $notValidatedData,
+                path: $path,
+                reason: ConstraintReason::FileNotFound,
+                status: ConstraintResultStatus::Invalid,
+            );
+        }
+
+        if (!is_readable($notValidatedData)) {
+            return new ConstraintResult(
+                castedData: $notValidatedData,
+                path: $path,
+                reason: ConstraintReason::FileNotReadable,
+                status: ConstraintResultStatus::Invalid,
+            );
+        }
+
+        return new ConstraintResult(
+            castedData: $notValidatedData,
+            path: $path,
+            reason: ConstraintReason::Ok,
+            status: ConstraintResultStatus::Valid,
+        );
+    }
+}
diff --git a/src/Constraint/FilenameConstraintTest.php b/src/Constraint/FilenameConstraintTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f168c71469224ca5ba21d19a3afdc4e79b1ec7bc
--- /dev/null
+++ b/src/Constraint/FilenameConstraintTest.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Distantmagic\Resonance\Constraint;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @coversNothing
+ *
+ * @internal
+ */
+final class FilenameConstraintTest extends TestCase
+{
+    public function test_invalid(): void
+    {
+        $constraint = new FilenameConstraint();
+
+        self::assertFalse($constraint->validate(__FILE__.'.not_exists')->status->isValid());
+    }
+
+    public function test_is_converted_optionally_to_json_schema(): void
+    {
+        $constraint = new FilenameConstraint();
+
+        self::assertEquals([
+            'type' => 'string',
+            'minLength' => 1,
+        ], $constraint->optional()->toJsonSchema());
+    }
+
+    public function test_is_converted_to_json_schema(): void
+    {
+        $constraint = new FilenameConstraint();
+
+        self::assertEquals([
+            'type' => 'string',
+            'minLength' => 1,
+        ], $constraint->toJsonSchema());
+    }
+
+    public function test_nullable_is_converted_to_json_schema(): void
+    {
+        $constraint = new FilenameConstraint();
+
+        self::assertEquals([
+            'type' => ['null', 'string'],
+            'minLength' => 1,
+            'default' => null,
+        ], $constraint->nullable()->toJsonSchema());
+    }
+
+    public function test_validates(): void
+    {
+        $constraint = new FilenameConstraint();
+
+        self::assertTrue($constraint->validate(__FILE__)->status->isValid());
+    }
+}
diff --git a/src/ConstraintReason.php b/src/ConstraintReason.php
index 0c990ff22a7fb8c8e81a84e64f72bb9eb4c7eeeb..ad77e8569b34352df4c73c347ee9c3f366605bd9 100644
--- a/src/ConstraintReason.php
+++ b/src/ConstraintReason.php
@@ -6,6 +6,8 @@ namespace Distantmagic\Resonance;
 
 enum ConstraintReason: string
 {
+    case FileNotFound = 'file_not_found';
+    case FileNotReadable = 'file_not_readable';
     case InvalidDataType = 'invalid_data_type';
     case InvalidEnumValue = 'invalid_enum_value';
     case InvalidFormat = 'invalid_format';
diff --git a/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php
index 62ef9accdfc222445a9ce1b1ead40d359caa9c10..7fd24e67c6656cf19a89158974ac6828c9364156 100644
--- a/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php
@@ -8,6 +8,7 @@ use Distantmagic\Resonance\ApplicationConfiguration;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
 use Distantmagic\Resonance\Constraint\EnumConstraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
 use Distantmagic\Resonance\Constraint\StringConstraint;
 use Distantmagic\Resonance\Environment;
@@ -30,7 +31,7 @@ final readonly class ApplicationConfigurationProvider extends ConfigurationProvi
         return new ObjectConstraint(
             properties: [
                 'env' => new EnumConstraint(Environment::values()),
-                'esbuild_metafile' => (new StringConstraint())->default('esbuild-meta.json'),
+                'esbuild_metafile' => (new FilenameConstraint())->nullable(),
                 'scheme' => (new EnumConstraint(['http', 'https']))->default('https'),
                 'url' => new StringConstraint(),
             ],
diff --git a/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php
index dddd3e439adbb2d72db86b39b093cdfda5579dd6..1e8bae3d7dba59e668a25082aabe55776825f515 100644
--- a/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php
@@ -8,6 +8,7 @@ use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
 use Distantmagic\Resonance\Constraint\BooleanConstraint;
 use Distantmagic\Resonance\Constraint\EnumConstraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\IntegerConstraint;
 use Distantmagic\Resonance\Constraint\MapConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
@@ -49,7 +50,7 @@ final readonly class DatabaseConfigurationProvider extends ConfigurationProvider
                 'pool_prefill' => (new BooleanConstraint())->default(true),
                 'pool_size' => new IntegerConstraint(),
                 'port' => (new IntegerConstraint())->nullable()->default(3306),
-                'unix_socket' => (new StringConstraint())->nullable(),
+                'unix_socket' => (new FilenameConstraint())->nullable(),
                 'username' => new StringConstraint(),
             ]
         );
diff --git a/src/SingletonProvider/ConfigurationProvider/GrpcConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/GrpcConfigurationProvider.php
index 940d9eebd5f08a0513ae4b82d62f361935b76267..3fd5eda7a637b5867d58e06550afea865e0a2a8f 100644
--- a/src/SingletonProvider/ConfigurationProvider/GrpcConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/GrpcConfigurationProvider.php
@@ -6,8 +6,8 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
-use Distantmagic\Resonance\Constraint\StringConstraint;
 use Distantmagic\Resonance\GrpcConfiguration;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
@@ -26,7 +26,8 @@ final readonly class GrpcConfigurationProvider extends ConfigurationProvider
     {
         return new ObjectConstraint(
             properties: [
-                'protoc_bin' => new StringConstraint(),
+                'grpc_php_plugin_bin' => new FilenameConstraint(),
+                'protoc_bin' => new FilenameConstraint(),
             ],
         );
     }
diff --git a/src/SingletonProvider/ConfigurationProvider/MailerConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/MailerConfigurationProvider.php
index 1aa00bf873ed0a492f47f38f113e5bae1b280720..6796e045793682d4da8fa7070221484fa0f67f2a 100644
--- a/src/SingletonProvider/ConfigurationProvider/MailerConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/MailerConfigurationProvider.php
@@ -7,6 +7,7 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Distantmagic\Resonance\Attribute\GrantsFeature;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\MapConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
 use Distantmagic\Resonance\Constraint\StringConstraint;
@@ -39,8 +40,8 @@ final readonly class MailerConfigurationProvider extends ConfigurationProvider
                 'dkim_domain_name' => (new StringConstraint())->nullable(),
                 'dkim_selector' => (new StringConstraint())->nullable(),
                 'dkim_signing_key_passphrase' => (new StringConstraint())->nullable(),
-                'dkim_signing_key_private' => (new StringConstraint())->nullable(),
-                'dkim_signing_key_public' => (new StringConstraint())->nullable(),
+                'dkim_signing_key_private' => (new FilenameConstraint())->nullable(),
+                'dkim_signing_key_public' => (new FilenameConstraint())->nullable(),
                 'transport_dsn' => new StringConstraint(),
             ]
         );
diff --git a/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php
index d0bbbe8cb5791a36de9ea67210dd90730de411fd..a1b0bc3121b75d5d1bfaa919bff60992db261e6a 100644
--- a/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php
@@ -8,6 +8,7 @@ use Defuse\Crypto\Key;
 use Distantmagic\Resonance\Attribute\GrantsFeature;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
 use Distantmagic\Resonance\Constraint\StringConstraint;
 use Distantmagic\Resonance\Feature;
@@ -36,10 +37,10 @@ final readonly class OAuth2ConfigurationProvider extends ConfigurationProvider
     {
         return new ObjectConstraint(
             properties: [
-                'encryption_key' => new StringConstraint(),
+                'encryption_key' => new FilenameConstraint(),
                 'jwt_signing_key_passphrase' => (new StringConstraint())->nullable(),
-                'jwt_signing_key_private' => new StringConstraint(),
-                'jwt_signing_key_public' => new StringConstraint(),
+                'jwt_signing_key_private' => new FilenameConstraint(),
+                'jwt_signing_key_public' => new FilenameConstraint(),
                 'session_key_authorization_request' => (new StringConstraint())->default('oauth2.authorization_request'),
                 'session_key_pkce' => (new StringConstraint())->default('oauth2.pkce'),
                 'session_key_state' => (new StringConstraint())->default('oauth2.state'),
diff --git a/src/SingletonProvider/ConfigurationProvider/SQLiteVSSConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/SQLiteVSSConfigurationProvider.php
index 478a48295ce9fbba140b571e5629ca2ff8a2d832..88fbe9231bc486b3ba7c7210f105d05dc4879c47 100644
--- a/src/SingletonProvider/ConfigurationProvider/SQLiteVSSConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/SQLiteVSSConfigurationProvider.php
@@ -6,8 +6,8 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
-use Distantmagic\Resonance\Constraint\StringConstraint;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Distantmagic\Resonance\SQLiteVSSConfiguration;
 
@@ -24,8 +24,8 @@ final readonly class SQLiteVSSConfigurationProvider extends ConfigurationProvide
     {
         return new ObjectConstraint(
             properties: [
-                'extension_vector0' => new StringConstraint(),
-                'extension_vss0' => new StringConstraint(),
+                'extension_vector0' => new FilenameConstraint(),
+                'extension_vss0' => new FilenameConstraint(),
             ],
         );
     }
diff --git a/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php
index c567231c4ba59c8b7f251c3118a43c83df25aafd..270fcec222da41b3ec12dc58e38479761e1d01d1 100644
--- a/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php
@@ -6,6 +6,7 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
 use Distantmagic\Resonance\Constraint\StringConstraint;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
@@ -28,9 +29,9 @@ final readonly class StaticPageConfigurationProvider extends ConfigurationProvid
         return new ObjectConstraint(
             properties: [
                 'base_url' => new StringConstraint(),
-                'esbuild_metafile' => new StringConstraint(),
-                'input_directory' => new StringConstraint(),
-                'output_directory' => new StringConstraint(),
+                'esbuild_metafile' => new FilenameConstraint(),
+                'input_directory' => new FilenameConstraint(),
+                'output_directory' => new FilenameConstraint(),
                 'sitemap' => new StringConstraint(),
             ]
         );
diff --git a/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php
index 45c99e67d1ce8593a6d04d03fab419fe74af767c..8fe0e045ad65d224647f7b65a679da02b1fd41b7 100644
--- a/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php
@@ -7,6 +7,7 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
 use Distantmagic\Resonance\Constraint\BooleanConstraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\IntegerConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
 use Distantmagic\Resonance\Constraint\StringConstraint;
@@ -35,8 +36,8 @@ final readonly class SwooleConfigurationProvider extends ConfigurationProvider
                 'log_level' => new IntegerConstraint(),
                 'log_requests' => (new BooleanConstraint())->default(false),
                 'port' => new IntegerConstraint(),
-                'ssl_cert_file' => (new StringConstraint())->default(null),
-                'ssl_key_file' => (new StringConstraint())->default(null),
+                'ssl_cert_file' => (new FilenameConstraint())->nullable(),
+                'ssl_key_file' => (new FilenameConstraint())->nullable(),
                 'task_worker_num' => (new IntegerConstraint())->default(4),
             ],
         );
diff --git a/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php
index ebaed71318b98e698a0b4c32f915792745012d75..e5f4f9f05489d88b19b5d0fd46e2fbe68815a69f 100644
--- a/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php
@@ -6,6 +6,7 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Constraint;
+use Distantmagic\Resonance\Constraint\FilenameConstraint;
 use Distantmagic\Resonance\Constraint\ObjectConstraint;
 use Distantmagic\Resonance\Constraint\StringConstraint;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
@@ -24,7 +25,7 @@ final readonly class TranslatorConfigurationProvider extends ConfigurationProvid
     {
         return new ObjectConstraint(
             properties: [
-                'base_directory' => new StringConstraint(),
+                'base_directory' => new FilenameConstraint(),
                 'default_primary_language' => new StringConstraint(),
             ]
         );
diff --git a/src/TwigEsbuildContext.php b/src/TwigEsbuildContext.php
index 812503a10bec4d726c45e22665af7d201dbdabc3..13c23d74c3acf5c35879c28446ea5ccb4144d51f 100644
--- a/src/TwigEsbuildContext.php
+++ b/src/TwigEsbuildContext.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use RuntimeException;
 use Swoole\Http\Request;
 use WeakMap;
 
@@ -16,7 +17,7 @@ readonly class TwigEsbuildContext
      */
     private WeakMap $entryPoints;
 
-    private EsbuildMeta $esbuildMeta;
+    private ?EsbuildMeta $esbuildMeta;
 
     public function __construct(
         ApplicationConfiguration $applicationConfiguration,
@@ -26,11 +27,17 @@ readonly class TwigEsbuildContext
          * @var WeakMap<Request,EsbuildMetaEntryPoints>
          */
         $this->entryPoints = new WeakMap();
-        $this->esbuildMeta = $esbuildMetaBuilder->build($applicationConfiguration->esbuildMetafile);
+        $this->esbuildMeta = is_string($applicationConfiguration->esbuildMetafile)
+            ? $esbuildMetaBuilder->build($applicationConfiguration->esbuildMetafile)
+            : null;
     }
 
     public function getEntryPoints(Request $request): EsbuildMetaEntryPoints
     {
+        if (is_null($this->esbuildMeta)) {
+            throw new RuntimeException("You need to provide application's esbuild metafile to use esbuild in Twig");
+        }
+
         if (!$this->entryPoints->offsetExists($request)) {
             $this->entryPoints->offsetSet($request, new EsbuildMetaEntryPoints($this->esbuildMeta));
         }