diff --git a/.eslintignore b/.eslintignore
index d12f8f18..235d9ab9 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,6 +3,8 @@ plugin/lib
 fixtures/
 demo
 
+plugin/src/templates/inject
+
 *~
 *.swp
 npm-debug.log
diff --git a/.eslintrc.js b/.eslintrc.js
index c396df13..fa3dbb59 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -7,10 +7,15 @@ module.exports = {
     'func-style': 'off',
     // This is compiled, so we can use modern syntax
     'node/no-unsupported-features/es-syntax': 'off',
+    'node/prefer-global/process': 'off',
+    'node/global-require': 'off',
     // This is a duplicate of `import/no-duplicates` but can handle "import type"
     'no-duplicate-imports': 'off',
+    'node/no-unpublished-import': 'off',
     'max-depth': ['error', 4],
+    complexity: 'off',
     'n/no-missing-import': 'off',
+    'n/global-require': 'off',
   },
   env: {
     jest: true,
@@ -21,15 +26,22 @@ module.exports = {
   overrides: [
     ...overrides,
     {
-      // Tests use lots of nested callbacks
-      files: ['*-test.js', '*.spec.js', '**/e2e-tests/*.js'],
+      files: [
+        '*-test.js',
+        '*.spec.js',
+        '**/e2e-tests/*.js',
+        '*-test.ts',
+        '*.spec.ts',
+        '**/e2e-tests/*.ts',
+      ],
       rules: {
         'max-nested-callbacks': 'off',
+        'ava/no-import-test-files': 'off',
       },
     },
     {
       // Templates import files from the site itself and needs lots of dynamic requires
-      files: ['plugin/src/templates/**/*'],
+      files: ['plugin/src/templates/**/*', 'plugin/test/unit/templates/**/*'],
       rules: {
         'n/no-unpublished-import': 'off',
         '@typescript-eslint/no-var-requires': 'off',
diff --git a/demo/netlify.toml b/demo/netlify.toml
index ecc11687..f3eceb95 100644
--- a/demo/netlify.toml
+++ b/demo/netlify.toml
@@ -1,7 +1,7 @@
 [build]
 command = "npm run build"
 publish = "public/"
-# ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../plugin/"
+ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF .."
 
 [[plugins]]
 package = "../plugin/src/index.ts"
diff --git a/package-lock.json b/package-lock.json
index defef278..eb39645d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3019,30 +3019,6 @@
         "node": ">=16.0.0"
       }
     },
-    "node_modules/@netlify/eslint-config-node/node_modules/eslint-plugin-n": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-14.0.0.tgz",
-      "integrity": "sha512-mNwplPLsbaKhHyA0fa/cy8j+oF6bF6l81hzBTWa6JOvPcMNAuIogk2ih6d9tYvWYzyUG+7ZFeChqbzdFpg2QrQ==",
-      "dev": true,
-      "dependencies": {
-        "eslint-plugin-es": "^4.1.0",
-        "eslint-utils": "^3.0.0",
-        "ignore": "^5.1.1",
-        "is-core-module": "^2.3.0",
-        "minimatch": "^3.0.4",
-        "resolve": "^1.10.1",
-        "semver": "^6.1.0"
-      },
-      "engines": {
-        "node": ">=12.22.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      },
-      "peerDependencies": {
-        "eslint": ">=7.0.0"
-      }
-    },
     "node_modules/@netlify/eslint-config-node/node_modules/execa": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz",
@@ -3087,18 +3063,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@netlify/eslint-config-node/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
     "node_modules/@netlify/eslint-config-node/node_modules/npm-run-path": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
@@ -3141,32 +3105,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@netlify/eslint-config-node/node_modules/resolve": {
-      "version": "1.22.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
-      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
-      "dev": true,
-      "dependencies": {
-        "is-core-module": "^2.9.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/@netlify/eslint-config-node/node_modules/semver": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
     "node_modules/@netlify/eslint-config-node/node_modules/strip-final-newline": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
@@ -6956,6 +6894,7 @@
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
       "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
       "dependencies": {
         "path-key": "^3.1.0",
         "shebang-command": "^2.0.0",
@@ -8474,20 +8413,18 @@
       }
     },
     "node_modules/eslint-plugin-n": {
-      "version": "15.5.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.5.1.tgz",
-      "integrity": "sha512-kAd+xhZm7brHoFLzKLB7/FGRFJNg/srmv67mqb7tto22rpr4wv/LV6RuXzAfv3jbab7+k1wi42PsIhGviywaaw==",
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-14.0.0.tgz",
+      "integrity": "sha512-mNwplPLsbaKhHyA0fa/cy8j+oF6bF6l81hzBTWa6JOvPcMNAuIogk2ih6d9tYvWYzyUG+7ZFeChqbzdFpg2QrQ==",
       "dev": true,
-      "peer": true,
       "dependencies": {
-        "builtins": "^5.0.1",
         "eslint-plugin-es": "^4.1.0",
         "eslint-utils": "^3.0.0",
         "ignore": "^5.1.1",
-        "is-core-module": "^2.11.0",
-        "minimatch": "^3.1.2",
-        "resolve": "^1.22.1",
-        "semver": "^7.3.8"
+        "is-core-module": "^2.3.0",
+        "minimatch": "^3.0.4",
+        "resolve": "^1.10.1",
+        "semver": "^6.1.0"
       },
       "engines": {
         "node": ">=12.22.0"
@@ -8504,7 +8441,6 @@
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
-      "peer": true,
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -8517,7 +8453,6 @@
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
       "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
       "dev": true,
-      "peer": true,
       "dependencies": {
         "is-core-module": "^2.9.0",
         "path-parse": "^1.0.7",
@@ -8531,19 +8466,12 @@
       }
     },
     "node_modules/eslint-plugin-n/node_modules/semver": {
-      "version": "7.3.8",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
-      "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
       "dev": true,
-      "peer": true,
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
       "bin": {
         "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
       }
     },
     "node_modules/eslint-plugin-promise": {
@@ -9084,6 +9012,7 @@
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
       "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
       "dependencies": {
         "cross-spawn": "^7.0.3",
         "get-stream": "^6.0.0",
@@ -9731,6 +9660,7 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
       "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
       "engines": {
         "node": ">=10"
       },
@@ -10249,6 +10179,7 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
       "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true,
       "engines": {
         "node": ">=10.17.0"
       }
@@ -10986,6 +10917,7 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
       "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true,
       "engines": {
         "node": ">=8"
       },
@@ -11125,7 +11057,8 @@
     "node_modules/isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
     },
     "node_modules/isobject": {
       "version": "3.0.1",
@@ -13834,7 +13767,8 @@
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
-      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
     },
     "node_modules/merge2": {
       "version": "1.4.1",
@@ -14632,6 +14566,7 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
       "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
       "dependencies": {
         "path-key": "^3.0.0"
       },
@@ -14838,6 +14773,7 @@
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
       "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
       "dependencies": {
         "mimic-fn": "^2.1.0"
       },
@@ -14852,6 +14788,7 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
       "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
       "engines": {
         "node": ">=6"
       }
@@ -15456,6 +15393,7 @@
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
       "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -16508,6 +16446,7 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
       "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
       "dependencies": {
         "shebang-regex": "^3.0.0"
       },
@@ -16519,6 +16458,7 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
       "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -16546,7 +16486,8 @@
     "node_modules/signal-exit": {
       "version": "3.0.7",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true
     },
     "node_modules/sisteransi": {
       "version": "1.0.5",
@@ -17094,6 +17035,7 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
       "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true,
       "engines": {
         "node": ">=6"
       }
@@ -18051,6 +17993,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
       "dependencies": {
         "isexe": "^2.0.0"
       },
diff --git a/package.json b/package.json
index a0397c5e..86be6153 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,8 @@
     "format:check-fix:prettier": "run-e format:check:prettier format:fix:prettier",
     "format:check:prettier": "cross-env-shell prettier --check $npm_package_config_prettier",
     "format:fix:prettier": "cross-env-shell prettier --write $npm_package_config_prettier",
-    "ava": "cross-env FORCE_COLOR=1 ava --verbose"
+    "ava": "cross-env FORCE_COLOR=1 ava --verbose",
+    "prepublishOnly": "echo 'You probably meant to publish the \"plugin\" package.' && exit 1"
   },
   "repository": {
     "type": "git",
diff --git a/plugin/package-lock.json b/plugin/package-lock.json
index a491964b..e4e3ee2a 100644
--- a/plugin/package-lock.json
+++ b/plugin/package-lock.json
@@ -1,14 +1,15 @@
 {
   "name": "@netlify/plugin-gatsby",
-  "version": "3.5.1",
+  "version": "3.5.2-merlin.4",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "@netlify/plugin-gatsby",
-      "version": "3.5.1",
+      "version": "3.5.2-merlin.4",
       "license": "MIT",
       "dependencies": {
+        "@gatsby-cloud-pkg/merlin-synchronizer": "latest",
         "@netlify/functions": "^1.3.0",
         "@netlify/ipx": "^1.3.3",
         "abortcontroller-polyfill": "^1.7.3",
@@ -2758,6 +2759,85 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/@gatsby-cloud-pkg/merlin-synchronizer": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/@gatsby-cloud-pkg/merlin-synchronizer/-/merlin-synchronizer-0.3.8.tgz",
+      "integrity": "sha512-Q/ty+qoBkOKnGnnFZx2wi/+g7BcyDl7q70x/iX6Pgx8y7W5e+ONrcIpR9inKjfWKz0pyvXSihibvb2rSCDJd+g==",
+      "dependencies": {
+        "fastq": "^1.13.0",
+        "fs-extra": "^10.1.0",
+        "got": "11.8.5",
+        "stream-json": "^1.7.4"
+      }
+    },
+    "node_modules/@gatsby-cloud-pkg/merlin-synchronizer/node_modules/@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/is?sponsor=1"
+      }
+    },
+    "node_modules/@gatsby-cloud-pkg/merlin-synchronizer/node_modules/cacheable-lookup": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+      "engines": {
+        "node": ">=10.6.0"
+      }
+    },
+    "node_modules/@gatsby-cloud-pkg/merlin-synchronizer/node_modules/decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "dependencies": {
+        "mimic-response": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@gatsby-cloud-pkg/merlin-synchronizer/node_modules/got": {
+      "version": "11.8.5",
+      "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz",
+      "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==",
+      "dependencies": {
+        "@sindresorhus/is": "^4.0.0",
+        "@szmarczak/http-timer": "^4.0.5",
+        "@types/cacheable-request": "^6.0.1",
+        "@types/responselike": "^1.0.0",
+        "cacheable-lookup": "^5.0.3",
+        "cacheable-request": "^7.0.2",
+        "decompress-response": "^6.0.0",
+        "http2-wrapper": "^1.0.0-beta.5.2",
+        "lowercase-keys": "^2.0.0",
+        "p-cancelable": "^2.0.0",
+        "responselike": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/got?sponsor=1"
+      }
+    },
+    "node_modules/@gatsby-cloud-pkg/merlin-synchronizer/node_modules/mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/@gatsbyjs/parcel-namer-relative-to-cwd": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/@gatsbyjs/parcel-namer-relative-to-cwd/-/parcel-namer-relative-to-cwd-2.3.1.tgz",
@@ -5775,7 +5855,6 @@
       "version": "4.0.6",
       "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
       "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
-      "dev": true,
       "dependencies": {
         "defer-to-connect": "^2.0.0"
       },
@@ -5854,7 +5933,6 @@
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
       "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==",
-      "dev": true,
       "dependencies": {
         "@types/http-cache-semantics": "*",
         "@types/keyv": "*",
@@ -5984,8 +6062,7 @@
     "node_modules/@types/http-cache-semantics": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
-      "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==",
-      "dev": true
+      "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
     },
     "node_modules/@types/http-proxy": {
       "version": "1.17.9",
@@ -6036,7 +6113,6 @@
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
       "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
-      "dev": true,
       "dependencies": {
         "@types/node": "*"
       }
@@ -6080,8 +6156,7 @@
     "node_modules/@types/node": {
       "version": "16.11.36",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
-      "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
-      "dev": true
+      "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA=="
     },
     "node_modules/@types/node-fetch": {
       "version": "2.6.2",
@@ -6147,7 +6222,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
       "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
-      "dev": true,
       "dependencies": {
         "@types/node": "*"
       }
@@ -8198,7 +8272,6 @@
       "version": "7.0.2",
       "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
       "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
-      "dev": true,
       "dependencies": {
         "clone-response": "^1.0.2",
         "get-stream": "^5.1.0",
@@ -8216,7 +8289,6 @@
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
       "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
-      "dev": true,
       "dependencies": {
         "pump": "^3.0.0"
       },
@@ -10235,7 +10307,6 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
       "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
-      "dev": true,
       "engines": {
         "node": ">=10"
       }
@@ -15746,8 +15817,7 @@
     "node_modules/http-cache-semantics": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
-      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
-      "dev": true
+      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
     },
     "node_modules/http-errors": {
       "version": "2.0.0",
@@ -15777,7 +15847,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
       "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
-      "dev": true,
       "dependencies": {
         "quick-lru": "^5.1.1",
         "resolve-alpn": "^1.0.0"
@@ -17026,8 +17095,7 @@
     "node_modules/json-buffer": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
-      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
-      "dev": true
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
     },
     "node_modules/json-loader": {
       "version": "0.5.7",
@@ -17141,7 +17209,6 @@
       "version": "4.5.2",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
       "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
-      "dev": true,
       "dependencies": {
         "json-buffer": "3.0.1"
       }
@@ -17671,7 +17738,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
       "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
-      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -18688,7 +18754,6 @@
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
       "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
-      "dev": true,
       "engines": {
         "node": ">=10"
       },
@@ -19280,7 +19345,6 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
       "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
-      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -21135,7 +21199,6 @@
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
       "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-      "dev": true,
       "engines": {
         "node": ">=10"
       },
@@ -21991,8 +22054,7 @@
     "node_modules/resolve-alpn": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
-      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
-      "dev": true
+      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
     },
     "node_modules/resolve-cwd": {
       "version": "3.0.0",
@@ -22026,7 +22088,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
       "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
-      "dev": true,
       "dependencies": {
         "lowercase-keys": "^2.0.0"
       }
@@ -23173,6 +23234,19 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/stream-chain": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
+      "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="
+    },
+    "node_modules/stream-json": {
+      "version": "1.7.5",
+      "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.7.5.tgz",
+      "integrity": "sha512-NSkoVduGakxZ8a+pTPUlcGEeAGQpWL9rKJhOFCV+J/QtdQUEU5vtBgVg6eJXn8JB8RZvpbJWZGvXkhz70MLWoA==",
+      "dependencies": {
+        "stream-chain": "^2.2.5"
+      }
+    },
     "node_modules/streamsearch": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
@@ -27456,6 +27530,60 @@
         }
       }
     },
+    "@gatsby-cloud-pkg/merlin-synchronizer": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/@gatsby-cloud-pkg/merlin-synchronizer/-/merlin-synchronizer-0.3.8.tgz",
+      "integrity": "sha512-Q/ty+qoBkOKnGnnFZx2wi/+g7BcyDl7q70x/iX6Pgx8y7W5e+ONrcIpR9inKjfWKz0pyvXSihibvb2rSCDJd+g==",
+      "requires": {
+        "fastq": "^1.13.0",
+        "fs-extra": "^10.1.0",
+        "got": "11.8.5",
+        "stream-json": "^1.7.4"
+      },
+      "dependencies": {
+        "@sindresorhus/is": {
+          "version": "4.6.0",
+          "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+          "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
+        },
+        "cacheable-lookup": {
+          "version": "5.0.4",
+          "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+          "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="
+        },
+        "decompress-response": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+          "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+          "requires": {
+            "mimic-response": "^3.1.0"
+          }
+        },
+        "got": {
+          "version": "11.8.5",
+          "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz",
+          "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==",
+          "requires": {
+            "@sindresorhus/is": "^4.0.0",
+            "@szmarczak/http-timer": "^4.0.5",
+            "@types/cacheable-request": "^6.0.1",
+            "@types/responselike": "^1.0.0",
+            "cacheable-lookup": "^5.0.3",
+            "cacheable-request": "^7.0.2",
+            "decompress-response": "^6.0.0",
+            "http2-wrapper": "^1.0.0-beta.5.2",
+            "lowercase-keys": "^2.0.0",
+            "p-cancelable": "^2.0.0",
+            "responselike": "^2.0.0"
+          }
+        },
+        "mimic-response": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+          "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+        }
+      }
+    },
     "@gatsbyjs/parcel-namer-relative-to-cwd": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/@gatsbyjs/parcel-namer-relative-to-cwd/-/parcel-namer-relative-to-cwd-2.3.1.tgz",
@@ -29484,7 +29612,6 @@
       "version": "4.0.6",
       "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
       "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
-      "dev": true,
       "requires": {
         "defer-to-connect": "^2.0.0"
       }
@@ -29554,7 +29681,6 @@
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
       "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==",
-      "dev": true,
       "requires": {
         "@types/http-cache-semantics": "*",
         "@types/keyv": "*",
@@ -29684,8 +29810,7 @@
     "@types/http-cache-semantics": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
-      "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==",
-      "dev": true
+      "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
     },
     "@types/http-proxy": {
       "version": "1.17.9",
@@ -29736,7 +29861,6 @@
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
       "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
-      "dev": true,
       "requires": {
         "@types/node": "*"
       }
@@ -29780,8 +29904,7 @@
     "@types/node": {
       "version": "16.11.36",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
-      "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
-      "dev": true
+      "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA=="
     },
     "@types/node-fetch": {
       "version": "2.6.2",
@@ -29847,7 +29970,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
       "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
-      "dev": true,
       "requires": {
         "@types/node": "*"
       }
@@ -31428,7 +31550,6 @@
       "version": "7.0.2",
       "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
       "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
-      "dev": true,
       "requires": {
         "clone-response": "^1.0.2",
         "get-stream": "^5.1.0",
@@ -31443,7 +31564,6 @@
           "version": "5.2.0",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
           "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
-          "dev": true,
           "requires": {
             "pump": "^3.0.0"
           }
@@ -33044,8 +33164,7 @@
     "defer-to-connect": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
-      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
-      "dev": true
+      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
     },
     "define-lazy-prop": {
       "version": "2.0.0",
@@ -37219,8 +37338,7 @@
     "http-cache-semantics": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
-      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
-      "dev": true
+      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
     },
     "http-errors": {
       "version": "2.0.0",
@@ -37243,7 +37361,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
       "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
-      "dev": true,
       "requires": {
         "quick-lru": "^5.1.1",
         "resolve-alpn": "^1.0.0"
@@ -38133,8 +38250,7 @@
     "json-buffer": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
-      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
-      "dev": true
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
     },
     "json-loader": {
       "version": "0.5.7",
@@ -38224,7 +38340,6 @@
       "version": "4.5.2",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
       "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
-      "dev": true,
       "requires": {
         "json-buffer": "3.0.1"
       }
@@ -38665,8 +38780,7 @@
     "lowercase-keys": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
-      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
-      "dev": true
+      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
     },
     "lru-cache": {
       "version": "4.0.0",
@@ -39437,8 +39551,7 @@
     "normalize-url": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
-      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
-      "dev": true
+      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
     },
     "npm-normalize-package-bin": {
       "version": "1.0.1",
@@ -39886,8 +39999,7 @@
     "p-cancelable": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
-      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
-      "dev": true
+      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="
     },
     "p-defer": {
       "version": "3.0.0",
@@ -41183,8 +41295,7 @@
     "quick-lru": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-      "dev": true
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
     },
     "quote-unquote": {
       "version": "1.0.0",
@@ -41836,8 +41947,7 @@
     "resolve-alpn": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
-      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
-      "dev": true
+      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
     },
     "resolve-cwd": {
       "version": "3.0.0",
@@ -41864,7 +41974,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
       "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
-      "dev": true,
       "requires": {
         "lowercase-keys": "^2.0.0"
       }
@@ -42769,6 +42878,19 @@
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
       "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
     },
+    "stream-chain": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
+      "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="
+    },
+    "stream-json": {
+      "version": "1.7.5",
+      "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.7.5.tgz",
+      "integrity": "sha512-NSkoVduGakxZ8a+pTPUlcGEeAGQpWL9rKJhOFCV+J/QtdQUEU5vtBgVg6eJXn8JB8RZvpbJWZGvXkhz70MLWoA==",
+      "requires": {
+        "stream-chain": "^2.2.5"
+      }
+    },
     "streamsearch": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
diff --git a/plugin/package.json b/plugin/package.json
index 52e1214b..e1daeaa4 100644
--- a/plugin/package.json
+++ b/plugin/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@netlify/plugin-gatsby",
-  "version": "3.5.1",
+  "version": "3.5.2-merlin.4",
   "description": "Netlify Build plugin - Run Gatsby seamlessly on Netlify",
   "source": "src/index.ts",
   "main": "lib/index.js",
@@ -40,6 +40,7 @@
     "prepare": "npm run build"
   },
   "dependencies": {
+    "@gatsby-cloud-pkg/merlin-synchronizer": "latest",
     "@netlify/functions": "^1.3.0",
     "@netlify/ipx": "^1.3.3",
     "abortcontroller-polyfill": "^1.7.3",
diff --git a/plugin/src/helpers/config.ts b/plugin/src/helpers/config.ts
index 315b02d9..7dde03ce 100644
--- a/plugin/src/helpers/config.ts
+++ b/plugin/src/helpers/config.ts
@@ -68,7 +68,7 @@ function loadGatsbyConfig({ gatsbyRoot, utils }): GatsbyConfig | never {
   }
 
   try {
-    // eslint-disable-next-line n/global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
+    // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires
     return require(gatsbyConfigFile) as GatsbyConfig
   } catch (error) {
     utils.build.failBuild('Could not load gatsby-config.js', { error })
diff --git a/plugin/src/helpers/files.ts b/plugin/src/helpers/files.ts
index 2d554c58..4b554d41 100644
--- a/plugin/src/helpers/files.ts
+++ b/plugin/src/helpers/files.ts
@@ -10,6 +10,7 @@ import {
   readFile,
   writeFile,
   readJson,
+  move,
 } from 'fs-extra'
 import { dirname, join, resolve } from 'pathe'
 import semver from 'semver'
@@ -27,6 +28,31 @@ const RELOCATABLE_BINARIES = [
   `node.abi${DEFAULT_LAMBDA_ABI}.glibc.node`,
 ]
 
+export const setupDecoupledSourcing = async (baseDir: string) => {
+  // Use experimental decoupled sourcing
+  if (
+    !process.env.GATSBY_CLOUD_DATALAYER ||
+    !process.env.CONTENT_CLOUD_ID ||
+    !process.env.RESOURCE_AUTH_JWT
+  ) {
+    console.error(
+      '👿 Decoupled sourcing was enabled without the required environment variables. Skipping.',
+    )
+  } else {
+    console.log('🧙 Using experimental decoupled sourcing')
+    await replaceGatsbySourceFile({
+      input: 'source-nodes-api-runner.js',
+      target: 'dist/utils/source-nodes-api-runner.js',
+      baseDir,
+    })
+    console.log('🪄 Patched Gatsby module source')
+  }
+}
+
+/**
+ * Perform various
+ */
+
 export const modifyFiles = async ({
   netlifyConfig,
   neededFunctions,
@@ -35,10 +61,60 @@ export const modifyFiles = async ({
   neededFunctions: FunctionList
 }): Promise<void> => {
   if (neededFunctions.includes('SSR') || neededFunctions.includes('DSG')) {
-    const root = dirname(netlifyConfig.build.publish)
-    await patchFile(root)
-    await relocateBinaries(root)
+    const baseDir = dirname(netlifyConfig.build.publish)
+    await patchFile(baseDir)
+    await relocateBinaries(baseDir)
+  }
+}
+
+/**
+ * Replace a source file in the Gatsby node module. Moves the original aside, adding a `.original` suffix.
+ */
+
+export const replaceGatsbySourceFile = async ({
+  input,
+  target,
+  baseDir,
+}: {
+  input: string
+  target: string
+  baseDir: string
+}): Promise<boolean> => {
+  const pathInGatsby = `gatsby/${target}`
+  const originalPath = require.resolve(pathInGatsby, { paths: [baseDir] })
+
+  if (!originalPath) {
+    console.warn(`Original file does not exist`)
+    return false
+  }
+
+  const backupPath = `${originalPath}.original`
+
+  if (existsSync(backupPath)) {
+    console.log(`File has already been moved. Skipping.`)
+    return true
   }
+
+  // This is getting the source file relative to the *compiled* version of this
+  const replacementFile = resolve(
+    __dirname,
+    '..',
+    '..',
+    'src',
+    'templates',
+    'inject',
+    input,
+  )
+
+  if (!existsSync(replacementFile)) {
+    console.warn(`Source file not found: ${replacementFile}`)
+  }
+
+  await move(originalPath, backupPath)
+
+  await copyFile(replacementFile, originalPath)
+
+  return true
 }
 
 /**
@@ -118,7 +194,7 @@ export async function checkPackageVersion(
  * This function ensures that the correct lmdb binaries are available.
  */
 
-// eslint-disable-next-line complexity, max-statements
+// eslint-disable-next-line max-statements
 export const relocateBinaries = async (baseDir: string): Promise<void> => {
   if (process.env.NETLIFY_LOCAL) {
     // We currently only handle CI builds
diff --git a/plugin/src/index.ts b/plugin/src/index.ts
index 84e73b41..0426ad2b 100644
--- a/plugin/src/index.ts
+++ b/plugin/src/index.ts
@@ -6,6 +6,7 @@ import { NetlifyPluginOptions } from '@netlify/build'
 import { stripIndent } from 'common-tags'
 import { existsSync } from 'fs-extra'
 import fetch from 'node-fetch'
+import { dirname, resolve } from 'pathe'
 
 import { normalizedCacheDir, restoreCache, saveCache } from './helpers/cache'
 import {
@@ -15,7 +16,7 @@ import {
   modifyConfig,
   shouldSkipBundlingDatastore,
 } from './helpers/config'
-import { modifyFiles } from './helpers/files'
+import { modifyFiles, setupDecoupledSourcing } from './helpers/files'
 import { deleteFunctions, writeFunctions } from './helpers/functions'
 import { checkZipSize } from './helpers/verification'
 
@@ -36,6 +37,12 @@ export async function onPreBuild({
   await restoreCache({ utils, publish: PUBLISH_DIR })
 
   await checkConfig({ utils, netlifyConfig })
+  if (
+    process.env.DECOUPLED_SOURCING === '1' ||
+    process.env.DECOUPLED_SOURCING === 'true'
+  ) {
+    await setupDecoupledSourcing(dirname(resolve(netlifyConfig.build.publish)))
+  }
 }
 
 export async function onBuild({
diff --git a/plugin/src/templates/inject/source-nodes-api-runner.js b/plugin/src/templates/inject/source-nodes-api-runner.js
new file mode 100644
index 00000000..7bba27cf
--- /dev/null
+++ b/plugin/src/templates/inject/source-nodes-api-runner.js
@@ -0,0 +1,176 @@
+/**
+ * This file is used to override node sourcing in Gatsby core so we can add decoupled sourcing to Gatsby
+ * This file was copied from Gatsby core and modified and will be copied back into node_modules/gatsby when Netlify does a cold-cache build.
+ * See 'helpers/files.ts'
+ */
+
+const GatsbyCacheLmdb = require(`./cache-lmdb`).default
+const { getDataStore } = require(`../datastore`)
+const reporter = require(`gatsby-cli/lib/reporter`)
+
+const SOURCE_PLUGIN_ALLOW_LIST = [
+  `gatsby-source-wordpress`,
+  `gatsby-source-drupal`,
+  `gatsby-source-contentful`,
+  `gatsby-source-shopify`,
+]
+
+let synchronizerCache
+let originalSourceNodesApiRunner
+let isFirstSource = true
+
+exports.sourceNodesApiRunner = async function sourceNodesApiRunner(args) {
+  const usingMerlin =
+    process.env.DECOUPLED_SOURCING === `true` ||
+    process.env.DECOUPLED_SOURCING === `1`
+
+  if (!originalSourceNodesApiRunner) {
+    originalSourceNodesApiRunner = require(`${__filename}.original`)
+  }
+
+  if (!usingMerlin) {
+    return originalSourceNodesApiRunner.sourceNodesApiRunner(args)
+  }
+
+  process.env.SITE_AUTH_JWT ||= process.env.RESOURCE_AUTH_JWT
+
+  const { synchronize } = require(`@gatsby-cloud-pkg/merlin-synchronizer`)
+  const { store } = require(`../redux`)
+
+  const reportContentHubSourcedLog = (
+    sourcedFromContentHub = [],
+    sourcedFromGatsby = [],
+  ) => {
+    reporter.info(
+      `[Gatsby] Finished sourcing data from Content Hub.\nPlugins sourced from Content Hub: ${sourcedFromContentHub}.\nPlugins sourced in Gatsby: ${sourcedFromGatsby.join(
+        `, `,
+      )}`,
+    )
+  }
+
+  synchronizerCache =
+    synchronizerCache ||
+    new GatsbyCacheLmdb({
+      name: `ledger-cache`,
+      encoding: `string`,
+    }).init()
+
+  const siteId = process.env.CONTENT_CLOUD_ID
+  if (!siteId) {
+    throw new Error(`Couldn't find CONTENT_CLOUD_ID`)
+  }
+
+  const sourcePluginAllowList = SOURCE_PLUGIN_ALLOW_LIST
+  if (process.env.EXPERIMENTAL_PLUGIN_ALLOW_LIST) {
+    sourcePluginAllowList.push(
+      ...process.env.EXPERIMENTAL_PLUGIN_ALLOW_LIST.split(`,`),
+    )
+  }
+
+  const supportedMerlinSourcePlugins = new Set()
+  const unsupportedMerlinSourcePlugins = new Set()
+  const sourceAllPlugins = !args.pluginName
+  const merlinAlreadySourcedPlugin = sourcePluginAllowList.includes(
+    args.pluginName,
+  )
+
+  store
+    .getState()
+    .flattenedPlugins.forEach((plugin) =>
+      sourcePluginAllowList.includes(plugin.name)
+        ? supportedMerlinSourcePlugins.add(plugin.name)
+        : unsupportedMerlinSourcePlugins.add(plugin.name),
+    )
+
+  if (isFirstSource) {
+    for (const node of getDataStore().iterateNodes()) {
+      if (supportedMerlinSourcePlugins.has(node.internal.owner)) {
+        store.dispatch({
+          type: `TOUCH_NODE`,
+          payload: node.id,
+        })
+      }
+    }
+    isFirstSource = false
+  }
+
+  let totalActions = 0
+
+  const contentHubSourcingActivity = reporter.activityTimer(
+    `Sourcing From Content Hub`,
+  )
+  contentHubSourcingActivity.start()
+
+  const { ledgerExists, lastSourcingUlid, sourcingConfigurationId } =
+    await synchronize({
+      cache: synchronizerCache,
+      gatsbySitePath: process.cwd(),
+      siteId,
+      // each action is dispatched immediately after it is received
+      handleAction: (action) => {
+        store.dispatch(action)
+        if (!action?.type?.startsWith(`SYNCHRONIZER`)) {
+          totalActions++
+        }
+      },
+    })
+
+  contentHubSourcingActivity.end()
+
+  if (process.send) {
+    process.send({ type: `MERLIN_ACTION`, payload: { ledgerExists } })
+  }
+  if (ledgerExists) {
+    reporter.info(
+      `Sourced successfully from Content Hub.\nTotal Gatsby Actions applied: ${totalActions}.\nSourcing Configuration Hash: ${sourcingConfigurationId}.\nSourced up to ledger id: ${lastSourcingUlid}`,
+    )
+    // if we are sourcing ALL plugins (no pluginName is provided) OR the provided plugin is supported
+    if (sourceAllPlugins || merlinAlreadySourcedPlugin) {
+      const gatsbyDBSyncingActivity = reporter.activityTimer(
+        `Syncing Data to Gatsby DB`,
+      )
+
+      gatsbyDBSyncingActivity.start()
+      // always run the internal data bridge plugin
+      const sourceNodesPromises = [
+        originalSourceNodesApiRunner.sourceNodesApiRunner({
+          ...args,
+          webhookBody: {},
+          pluginName: `internal-data-bridge`,
+        }),
+      ]
+
+      // source all unsupported plugins if we are sourcing all plugins
+      // If merlinAlreadySourcedPlugin, we don't need to source any plugins other than the internal data bridge
+      if (sourceAllPlugins) {
+        sourceNodesPromises.push(
+          ...[...unsupportedMerlinSourcePlugins].map((pluginName) =>
+            originalSourceNodesApiRunner.sourceNodesApiRunner({
+              ...args,
+              webhookBody: {},
+              pluginName,
+            }),
+          ),
+        )
+      }
+      await Promise.all(sourceNodesPromises)
+      gatsbyDBSyncingActivity.end()
+      sourceAllPlugins
+        ? reportContentHubSourcedLog(
+            [...supportedMerlinSourcePlugins],
+            [...unsupportedMerlinSourcePlugins],
+          )
+        : reportContentHubSourcedLog([args.pluginName, `internal-data-bridge`])
+    } else {
+      // Update from a non supported plugin
+      originalSourceNodesApiRunner.sourceNodesApiRunner(args)
+      reportContentHubSourcedLog([], [args.pluginName])
+    }
+  } else {
+    reporter.info(
+      `Unable to pull data from Content Hub. Sourcing Configuration Hash: ${sourcingConfigurationId}. Attempting to source in Gatsby now...`,
+    )
+    // when no ledger exists, run original sourcing - better luck next time
+    return originalSourceNodesApiRunner.sourceNodesApiRunner(args)
+  }
+}
diff --git a/plugin/test/unit/helpers/config.spec.ts b/plugin/test/unit/helpers/config.spec.ts
index f8ade095..403f3f58 100644
--- a/plugin/test/unit/helpers/config.spec.ts
+++ b/plugin/test/unit/helpers/config.spec.ts
@@ -1,4 +1,3 @@
-/* eslint-disable ava/no-import-test-files */
 import { resolve, join } from 'path'
 import process from 'process'
 
@@ -272,4 +271,3 @@ describe('createMetadataFileAndCopyDatastore', () => {
     TEST_TIMEOUT,
   )
 })
-/* eslint-enable ava/no-import-test-files */
diff --git a/plugin/test/unit/index.spec.ts b/plugin/test/unit/index.spec.ts
index e68afd6e..0c6a4f8b 100644
--- a/plugin/test/unit/index.spec.ts
+++ b/plugin/test/unit/index.spec.ts
@@ -1,4 +1,3 @@
-/* eslint-disable max-nested-callbacks, ava/no-import-test-files */
 import process from 'process'
 
 import {
@@ -266,4 +265,3 @@ describe('plugin', () => {
     })
   })
 })
-/* eslint-enable max-nested-callbacks, ava/no-import-test-files */
diff --git a/plugin/test/unit/templates/utils.spec.ts b/plugin/test/unit/templates/utils.spec.ts
index bdca3c90..23ca7c5e 100644
--- a/plugin/test/unit/templates/utils.spec.ts
+++ b/plugin/test/unit/templates/utils.spec.ts
@@ -1,4 +1,3 @@
-/* eslint-disable ava/no-import-test-files */
 import { tmpdir } from 'os'
 import { resolve, join, dirname } from 'path'
 
@@ -173,4 +172,3 @@ describe('downloadFile', () => {
     )
   })
 })
-/* eslint-enable ava/no-import-test-files */
diff --git a/plugin/tsconfig.json b/plugin/tsconfig.json
index 6df35036..d61d2b09 100644
--- a/plugin/tsconfig.json
+++ b/plugin/tsconfig.json
@@ -99,5 +99,6 @@
     // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
     "skipLibCheck": true /* Skip type checking all .d.ts files. */
   },
-  "include": ["src/**/*.ts"]
+  "include": ["src/**/*.ts"],
+  "exclude": ["src/templates/inject/*"]
 }