Repository: sveltejs/kit
Pull request #6896 (2022-09-19 18:39:17)

bundle with rollup instead of esbuild
Alternative to #6855, which is itself an alternative to #6797.

With this PR, we bundle `adapter-node` apps with Rollup instead of esbuild. It's slower, but avoids https://github.com/evanw/esbuild/issues/1921 which doesn't look like it'll be fixed any time soon (even though there's a PR — https://github.com/evanw/esbuild/pull/2067), and means that we can deal with both ESM and CJS dependencies without adding more configuration.

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it solves.
- [ ] Ideally, include a test that fails without this PR but passes with it.

### Tests
- [ ] Run the tests with `pnpm test` and lint the project with `pnpm lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. All changesets should be `patch` until SvelteKit 1.0


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: 99d120c15905ea1109fc1814f5bf5a261bb82b4d

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                   | Type  |
| ---------------------- | ----- |
| @sveltejs/adapter-node | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/sveltejs/kit/new/bundle-with-rollup?filename=.changeset/short-chicken-trade.md&value=---%0A%22%40sveltejs%2Fadapter-node%22%3A%20patch%0A---%0A%0Abundle%20with%20rollup%20instead%20of%20esbuild%0A)



Patch:
diff --git a/.changeset/cool-beans-only.md b/.changeset/cool-beans-only.md
new file mode 100644
index 000000000..755343640
--- /dev/null
+++ b/.changeset/cool-beans-only.md
@@ -0,0 +1,5 @@
+---
+"@sveltejs/kit": patch
+---
+
+fix `write_types` on windows using posixify()
diff --git a/.changeset/cyan-years-live.md b/.changeset/cyan-years-live.md
new file mode 100644
index 000000000..0ed5696bb
--- /dev/null
+++ b/.changeset/cyan-years-live.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Prevent caching of `__data.js` files
diff --git a/.changeset/odd-beds-smile.md b/.changeset/odd-beds-smile.md
new file mode 100644
index 000000000..63aa75272
--- /dev/null
+++ b/.changeset/odd-beds-smile.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/adapter-node': patch
+---
+
+Bundle with rollup instead of esbuild
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 51192c0df..08cce8d6b 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -179,6 +179,7 @@
     "cold-shirts-lick",
     "cold-tables-shake",
     "cool-bananas-share",
+    "cool-beans-only",
     "cool-birds-attack",
     "cool-crabs-listen",
     "cool-drinks-press",
@@ -215,6 +216,7 @@
     "cyan-roses-act",
     "cyan-schools-kiss",
     "cyan-seals-trade",
+    "cyan-years-live",
     "dirty-bikes-join",
     "dirty-ligers-protect",
     "dry-baboons-turn",
@@ -981,6 +983,7 @@
     "silver-hounds-clean",
     "silver-lemons-fail",
     "silver-needles-judge",
+    "silver-pugs-worry",
     "silver-rice-flash",
     "silver-scissors-peel",
     "silver-toes-cheer",
@@ -1071,6 +1074,7 @@
     "spotty-phones-love",
     "spotty-ties-love",
     "spotty-timers-fix",
+    "spotty-trainers-switch",
     "spotty-vans-tickle",
     "stale-apples-cry",
     "stale-crabs-carry",
@@ -1362,6 +1366,7 @@
     "young-pumpkins-approve",
     "young-scissors-collect",
     "young-students-chew",
+    "young-students-relate",
     "young-swans-burn",
     "young-ways-fail"
   ]
diff --git a/.changeset/silent-jeans-vanish.md b/.changeset/silent-jeans-vanish.md
new file mode 100644
index 000000000..44b09d9c1
--- /dev/null
+++ b/.changeset/silent-jeans-vanish.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] symlink routes
diff --git a/.changeset/silver-pugs-worry.md b/.changeset/silver-pugs-worry.md
new file mode 100644
index 000000000..27e98e25a
--- /dev/null
+++ b/.changeset/silver-pugs-worry.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] tighten up handling and documentation around 404
diff --git a/.changeset/spotty-trainers-switch.md b/.changeset/spotty-trainers-switch.md
new file mode 100644
index 000000000..2cd773e1d
--- /dev/null
+++ b/.changeset/spotty-trainers-switch.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Decode HTML entities in `href` attributes when crawling
diff --git a/.changeset/young-students-relate.md b/.changeset/young-students-relate.md
new file mode 100644
index 000000000..06b05a32b
--- /dev/null
+++ b/.changeset/young-students-relate.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Make url property getters non-enumerable
diff --git a/documentation/docs/04-advanced-routing.md b/documentation/docs/04-advanced-routing.md
index 05c1c813f..2f9d67abe 100644
--- a/documentation/docs/04-advanced-routing.md
+++ b/documentation/docs/04-advanced-routing.md
@@ -22,7 +22,11 @@ If the number of route segments is unknown, you can use rest syntax — for exam
 }
 ```
 
-This also allows you to render custom 404s. Given these routes...
+> `src/routes/a/[...rest]/z/+page.svelte` will match `/a/z` (i.e. there's no parameter at all) as well as `/a/b/z` and `/a/b/c/z` and so on. Make sure you check that the value of the rest parameter is valid, for example using a [matcher](#matching).
+
+#### 404 pages
+
+Rest parameters also allow you to render custom 404s. Given these routes...
 
 ```
 src/routes/
@@ -47,7 +51,17 @@ src/routes/
 └ +error.svelte
 ```
 
-> `src/routes/a/[...rest]/z/+page.svelte` will match `/a/z` (i.e. there's no parameter at all) as well as `/a/b/z` and `/a/b/c/z` and so on. Make sure you check that the value of the rest parameter is valid, for example using a [matcher](#matching).
+```js
+/// file: src/routes/marx-brothers/[...path]/+page.js
+import { error } from '@sveltejs/kit';
+
+/** @type {import('./$types').PageLoad} */
+export function load(event) {
+	throw error(404, 'Not Found');
+}
+```
+
+> If you don't handle 404 cases, they will appear in [`handleError`](/docs/hooks#shared-hooks-handleerror)
 
 ### Matching
 
diff --git a/documentation/docs/07-hooks.md b/documentation/docs/07-hooks.md
index 1b1dd3adb..8ede315c3 100644
--- a/documentation/docs/07-hooks.md
+++ b/documentation/docs/07-hooks.md
@@ -136,7 +136,7 @@ The following can be added to `src/hooks.server.js` _and_ `src/hooks.client.js`:
 If an unexpected error is thrown during loading or rendering, this function will be called with the `error` and the `event`. This allows for two things:
 
 - you can log the error
-- you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces. The returned value, which defaults to `{ message: 'Internal Error' }`, becomes the value of `$page.error`. To make this type-safe, you can customize the expected shape by declaring an `App.PageError` interface (which must include `message: string`, to guarantee sensible fallback behavior).
+- you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces. The returned value becomes the value of `$page.error`. It defaults to `{ message: 'Not Found' }` in case of a 404 (you can detect them through `event.routeId` being `null`) and to `{ message: 'Internal Error' }` for everything else. To make this type-safe, you can customize the expected shape by declaring an `App.PageError` interface (which must include `message: string`, to guarantee sensible fallback behavior).
 
 The following code shows an example of typing the error shape as `{ message: string; code: string }` and returning it accordingly from the `handleError` functions:
 
diff --git a/package.json b/package.json
index 817041bcc..6109081fc 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
 		"turbo": "^1.3.4",
 		"typescript": "^4.8.2"
 	},
-	"packageManager": "pnpm@7.12.0",
+	"packageManager": "pnpm@7.12.1",
 	"engines": {
 		"pnpm": "^7.0.0"
 	},
diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js
index cc3d0da78..17bb5310c 100644
--- a/packages/adapter-node/index.js
+++ b/packages/adapter-node/index.js
@@ -1,6 +1,9 @@
 import { readFileSync, writeFileSync } from 'fs';
 import { fileURLToPath } from 'url';
-import * as esbuild from 'esbuild';
+import { rollup } from 'rollup';
+import { nodeResolve } from '@rollup/plugin-node-resolve';
+import commonjs from '@rollup/plugin-commonjs';
+import json from '@rollup/plugin-json';
 
 const files = fileURLToPath(new URL('./files', import.meta.url).href);
 
@@ -49,16 +52,23 @@ export default function (opts = {}) {
 
 			const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
 
-			await esbuild.build({
-				platform: 'node',
-				sourcemap: 'linked',
-				target: 'es2022',
-				entryPoints: [`${tmp}/index.js`, `${tmp}/manifest.js`],
-				outdir: `${out}/server`,
-				splitting: true,
+			// we bundle the Vite output so that deployments only need
+			// their production dependencies. Anything in devDependencies
+			// will get included in the bundled code
+			const bundle = await rollup({
+				input: {
+					index: `${tmp}/index.js`,
+					manifest: `${tmp}/manifest.js`
+				},
+				external: [...Object.keys(pkg.dependencies || {})],
+				plugins: [nodeResolve(), commonjs(), json()]
+			});
+
+			await bundle.write({
+				dir: `${out}/server`,
 				format: 'esm',
-				bundle: true,
-				external: [...Object.keys(pkg.dependencies || {})]
+				sourcemap: true,
+				chunkFileNames: `chunks/[name]-[hash].js`
 			});
 
 			builder.copy(files, out, {
diff --git a/packages/adapter-node/package.json b/packages/adapter-node/package.json
index c5d40c7fb..c947ef5f0 100644
--- a/packages/adapter-node/package.json
+++ b/packages/adapter-node/package.json
@@ -32,7 +32,6 @@
 		"prepublishOnly": "npm run build"
 	},
 	"devDependencies": {
-		"@rollup/plugin-json": "^4.1.0",
 		"@sveltejs/kit": "workspace:*",
 		"@types/node": "^16.11.36",
 		"c8": "^7.11.3",
@@ -45,6 +44,8 @@
 		"uvu": "^0.5.3"
 	},
 	"dependencies": {
-		"esbuild": "^0.15.7"
+		"@rollup/plugin-commonjs": "^22.0.1",
+		"@rollup/plugin-json": "^4.1.0",
+		"@rollup/plugin-node-resolve": "^14.1.0"
 	}
 }
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 7b467086e..a1848b42a 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,27 @@
 # @sveltejs/kit
 
+## 1.0.0-next.490
+
+### Patch Changes
+
+- fix `write_types` on windows using posixify() ([#6913](https://github.com/sveltejs/kit/pull/6913))
+
+* Prevent caching of `__data.js` files ([#6904](https://github.com/sveltejs/kit/pull/6904))
+
+- Make url property getters non-enumerable ([#6909](https://github.com/sveltejs/kit/pull/6909))
+
+## 1.0.0-next.489
+
+### Patch Changes
+
+- [fix] tighten up handling and documentation around 404 ([#6897](https://github.com/sveltejs/kit/pull/6897))
+
+## 1.0.0-next.488
+
+### Patch Changes
+
+- Decode HTML entities in `href` attributes when crawling ([#6891](https://github.com/sveltejs/kit/pull/6891))
+
 ## 1.0.0-next.487
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index b25610318..4d18742c0 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.487",
+	"version": "1.0.0-next.490",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js
index d0e35d59f..91ec76997 100644
--- a/packages/kit/src/core/generate_manifest/index.js
+++ b/packages/kit/src/core/generate_manifest/index.js
@@ -1,5 +1,6 @@
 import { s } from '../../utils/misc.js';
 import { get_mime_lookup } from '../utils.js';
+import { resolve_symlinks } from '../../exports/vite/build/utils.js';
 
 /**
  * Generates the data used to write the server-side manifest.js file. This data is used in the Vite
@@ -65,7 +66,7 @@ export function generate_manifest({ build_data, relative_path, routes, format =
 					names: ${s(route.names)},
 					types: ${s(route.types)},
 					page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${route.page.leaf} }` : 'null'},
-					endpoint: ${route.endpoint ? loader(`${relative_path}/${build_data.server.vite_manifest[route.endpoint.file].file}`) : 'null'}
+					endpoint: ${route.endpoint ? loader(`${relative_path}/${resolve_symlinks(build_data.server.vite_manifest, route.endpoint.file).chunk.file}`) : 'null'}
 				}`;
 				}).filter(Boolean).join(',\n\t\t\t\t')}
 			],
diff --git a/packages/kit/src/core/prerender/crawl.js b/packages/kit/src/core/prerender/crawl.js
index b74e558d6..70c264658 100644
--- a/packages/kit/src/core/prerender/crawl.js
+++ b/packages/kit/src/core/prerender/crawl.js
@@ -1,3 +1,5 @@
+import { decode } from './entities.js';
+
 const DOCTYPE = 'DOCTYPE';
 const CDATA_OPEN = '[CDATA[';
 const CDATA_CLOSE = ']]>';
@@ -147,6 +149,8 @@ export function crawl(html) {
 								i -= 1;
 							}
 
+							value = decode(value);
+
 							if (name === 'href') {
 								href = value;
 							} else if (name === 'rel') {
diff --git a/packages/kit/src/core/prerender/entities.js b/packages/kit/src/core/prerender/entities.js
new file mode 100644
index 000000000..91888a95b
--- /dev/null
+++ b/packages/kit/src/core/prerender/entities.js
@@ -0,0 +1,2252 @@
+/**
+ * A list of all named HTML entities, from https://html.spec.whatwg.org/multipage/named-characters.html
+ * @type {Record<string, string>}
+ */
+const entities = {
+	AElig: 'Æ',
+	'AElig;': 'Æ',
+	AMP: '&',
+	'AMP;': '&',
+	Aacute: 'Á',
+	'Aacute;': 'Á',
+	'Abreve;': 'Ă',
+	Acirc: 'Â',
+	'Acirc;': 'Â',
+	'Acy;': 'А',
+	'Afr;': '𝔄',
+	Agrave: 'À',
+	'Agrave;': 'À',
+	'Alpha;': 'Α',
+	'Amacr;': 'Ā',
+	'And;': '⩓',
+	'Aogon;': 'Ą',
+	'Aopf;': '𝔸',
+	'ApplyFunction;': '⁡',
+	Aring: 'Å',
+	'Aring;': 'Å',
+	'Ascr;': '𝒜',
+	'Assign;': '≔',
+	Atilde: 'Ã',
+	'Atilde;': 'Ã',
+	Auml: 'Ä',
+	'Auml;': 'Ä',
+	'Backslash;': '∖',
+	'Barv;': '⫧',
+	'Barwed;': '⌆',
+	'Bcy;': 'Б',
+	'Because;': '∵',
+	'Bernoullis;': 'ℬ',
+	'Beta;': 'Β',
+	'Bfr;': '𝔅',
+	'Bopf;': '𝔹',
+	'Breve;': '˘',
+	'Bscr;': 'ℬ',
+	'Bumpeq;': '≎',
+	'CHcy;': 'Ч',
+	COPY: '©',
+	'COPY;': '©',
+	'Cacute;': 'Ć',
+	'Cap;': '⋒',
+	'CapitalDifferentialD;': 'ⅅ',
+	'Cayleys;': 'ℭ',
+	'Ccaron;': 'Č',
+	Ccedil: 'Ç',
+	'Ccedil;': 'Ç',
+	'Ccirc;': 'Ĉ',
+	'Cconint;': '∰',
+	'Cdot;': 'Ċ',
+	'Cedilla;': '¸',
+	'CenterDot;': '·',
+	'Cfr;': 'ℭ',
+	'Chi;': 'Χ',
+	'CircleDot;': '⊙',
+	'CircleMinus;': '⊖',
+	'CirclePlus;': '⊕',
+	'CircleTimes;': '⊗',
+	'ClockwiseContourIntegral;': '∲',
+	'CloseCurlyDoubleQuote;': '”',
+	'CloseCurlyQuote;': '’',
+	'Colon;': '∷',
+	'Colone;': '⩴',
+	'Congruent;': '≡',
+	'Conint;': '∯',
+	'ContourIntegral;': '∮',
+	'Copf;': 'ℂ',
+	'Coproduct;': '∐',
+	'CounterClockwiseContourIntegral;': '∳',
+	'Cross;': '⨯',
+	'Cscr;': '𝒞',
+	'Cup;': '⋓',
+	'CupCap;': '≍',
+	'DD;': 'ⅅ',
+	'DDotrahd;': '⤑',
+	'DJcy;': 'Ђ',
+	'DScy;': 'Ѕ',
+	'DZcy;': 'Џ',
+	'Dagger;': '‡',
+	'Darr;': '↡',
+	'Dashv;': '⫤',
+	'Dcaron;': 'Ď',
+	'Dcy;': 'Д',
+	'Del;': '∇',
+	'Delta;': 'Δ',
+	'Dfr;': '𝔇',
+	'DiacriticalAcute;': '´',
+	'DiacriticalDot;': '˙',
+	'DiacriticalDoubleAcute;': '˝',
+	'DiacriticalGrave;': '`',
+	'DiacriticalTilde;': '˜',
+	'Diamond;': '⋄',
+	'DifferentialD;': 'ⅆ',
+	'Dopf;': '𝔻',
+	'Dot;': '¨',
+	'DotDot;': '⃜',
+	'DotEqual;': '≐',
+	'DoubleContourIntegral;': '∯',
+	'DoubleDot;': '¨',
+	'DoubleDownArrow;': '⇓',
+	'DoubleLeftArrow;': '⇐',
+	'DoubleLeftRightArrow;': '⇔',
+	'DoubleLeftTee;': '⫤',
+	'DoubleLongLeftArrow;': '⟸',
+	'DoubleLongLeftRightArrow;': '⟺',
+	'DoubleLongRightArrow;': '⟹',
+	'DoubleRightArrow;': '⇒',
+	'DoubleRightTee;': '⊨',
+	'DoubleUpArrow;': '⇑',
+	'DoubleUpDownArrow;': '⇕',
+	'DoubleVerticalBar;': '∥',
+	'DownArrow;': '↓',
+	'DownArrowBar;': '⤓',
+	'DownArrowUpArrow;': '⇵',
+	'DownBreve;': '̑',
+	'DownLeftRightVector;': '⥐',
+	'DownLeftTeeVector;': '⥞',
+	'DownLeftVector;': '↽',
+	'DownLeftVectorBar;': '⥖',
+	'DownRightTeeVector;': '⥟',
+	'DownRightVector;': '⇁',
+	'DownRightVectorBar;': '⥗',
+	'DownTee;': '⊤',
+	'DownTeeArrow;': '↧',
+	'Downarrow;': '⇓',
+	'Dscr;': '𝒟',
+	'Dstrok;': 'Đ',
+	'ENG;': 'Ŋ',
+	ETH: 'Ð',
+	'ETH;': 'Ð',
+	Eacute: 'É',
+	'Eacute;': 'É',
+	'Ecaron;': 'Ě',
+	Ecirc: 'Ê',
+	'Ecirc;': 'Ê',
+	'Ecy;': 'Э',
+	'Edot;': 'Ė',
+	'Efr;': '𝔈',
+	Egrave: 'È',
+	'Egrave;': 'È',
+	'Element;': '∈',
+	'Emacr;': 'Ē',
+	'EmptySmallSquare;': '◻',
+	'EmptyVerySmallSquare;': '▫',
+	'Eogon;': 'Ę',
+	'Eopf;': '𝔼',
+	'Epsilon;': 'Ε',
+	'Equal;': '⩵',
+	'EqualTilde;': '≂',
+	'Equilibrium;': '⇌',
+	'Escr;': 'ℰ',
+	'Esim;': '⩳',
+	'Eta;': 'Η',
+	Euml: 'Ë',
+	'Euml;': 'Ë',
+	'Exists;': '∃',
+	'ExponentialE;': 'ⅇ',
+	'Fcy;': 'Ф',
+	'Ffr;': '𝔉',
+	'FilledSmallSquare;': '◼',
+	'FilledVerySmallSquare;': '▪',
+	'Fopf;': '𝔽',
+	'ForAll;': '∀',
+	'Fouriertrf;': 'ℱ',
+	'Fscr;': 'ℱ',
+	'GJcy;': 'Ѓ',
+	GT: '>',
+	'GT;': '>',
+	'Gamma;': 'Γ',
+	'Gammad;': 'Ϝ',
+	'Gbreve;': 'Ğ',
+	'Gcedil;': 'Ģ',
+	'Gcirc;': 'Ĝ',
+	'Gcy;': 'Г',
+	'Gdot;': 'Ġ',
+	'Gfr;': '𝔊',
+	'Gg;': '⋙',
+	'Gopf;': '𝔾',
+	'GreaterEqual;': '≥',
+	'GreaterEqualLess;': '⋛',
+	'GreaterFullEqual;': '≧',
+	'GreaterGreater;': '⪢',
+	'GreaterLess;': '≷',
+	'GreaterSlantEqual;': '⩾',
+	'GreaterTilde;': '≳',
+	'Gscr;': '𝒢',
+	'Gt;': '≫',
+	'HARDcy;': 'Ъ',
+	'Hacek;': 'ˇ',
+	'Hat;': '^',
+	'Hcirc;': 'Ĥ',
+	'Hfr;': 'ℌ',
+	'HilbertSpace;': 'ℋ',
+	'Hopf;': 'ℍ',
+	'HorizontalLine;': '─',
+	'Hscr;': 'ℋ',
+	'Hstrok;': 'Ħ',
+	'HumpDownHump;': '≎',
+	'HumpEqual;': '≏',
+	'IEcy;': 'Е',
+	'IJlig;': 'Ĳ',
+	'IOcy;': 'Ё',
+	Iacute: 'Í',
+	'Iacute;': 'Í',
+	Icirc: 'Î',
+	'Icirc;': 'Î',
+	'Icy;': 'И',
+	'Idot;': 'İ',
+	'Ifr;': 'ℑ',
+	Igrave: 'Ì',
+	'Igrave;': 'Ì',
+	'Im;': 'ℑ',
+	'Imacr;': 'Ī',
+	'ImaginaryI;': 'ⅈ',
+	'Implies;': '⇒',
+	'Int;': '∬',
+	'Integral;': '∫',
+	'Intersection;': '⋂',
+	'InvisibleComma;': '⁣',
+	'InvisibleTimes;': '⁢',
+	'Iogon;': 'Į',
+	'Iopf;': '𝕀',
+	'Iota;': 'Ι',
+	'Iscr;': 'ℐ',
+	'Itilde;': 'Ĩ',
+	'Iukcy;': 'І',
+	Iuml: 'Ï',
+	'Iuml;': 'Ï',
+	'Jcirc;': 'Ĵ',
+	'Jcy;': 'Й',
+	'Jfr;': '𝔍',
+	'Jopf;': '𝕁',
+	'Jscr;': '𝒥',
+	'Jsercy;': 'Ј',
+	'Jukcy;': 'Є',
+	'KHcy;': 'Х',
+	'KJcy;': 'Ќ',
+	'Kappa;': 'Κ',
+	'Kcedil;': 'Ķ',
+	'Kcy;': 'К',
+	'Kfr;': '𝔎',
+	'Kopf;': '𝕂',
+	'Kscr;': '𝒦',
+	'LJcy;': 'Љ',
+	LT: '<',
+	'LT;': '<',
+	'Lacute;': 'Ĺ',
+	'Lambda;': 'Λ',
+	'Lang;': '⟪',
+	'Laplacetrf;': 'ℒ',
+	'Larr;': '↞',
+	'Lcaron;': 'Ľ',
+	'Lcedil;': 'Ļ',
+	'Lcy;': 'Л',
+	'LeftAngleBracket;': '⟨',
+	'LeftArrow;': '←',
+	'LeftArrowBar;': '⇤',
+	'LeftArrowRightArrow;': '⇆',
+	'LeftCeiling;': '⌈',
+	'LeftDoubleBracket;': '⟦',
+	'LeftDownTeeVector;': '⥡',
+	'LeftDownVector;': '⇃',
+	'LeftDownVectorBar;': '⥙',
+	'LeftFloor;': '⌊',
+	'LeftRightArrow;': '↔',
+	'LeftRightVector;': '⥎',
+	'LeftTee;': '⊣',
+	'LeftTeeArrow;': '↤',
+	'LeftTeeVector;': '⥚',
+	'LeftTriangle;': '⊲',
+	'LeftTriangleBar;': '⧏',
+	'LeftTriangleEqual;': '⊴',
+	'LeftUpDownVector;': '⥑',
+	'LeftUpTeeVector;': '⥠',
+	'LeftUpVector;': '↿',
+	'LeftUpVectorBar;': '⥘',
+	'LeftVector;': '↼',
+	'LeftVectorBar;': '⥒',
+	'Leftarrow;': '⇐',
+	'Leftrightarrow;': '⇔',
+	'LessEqualGreater;': '⋚',
+	'LessFullEqual;': '≦',
+	'LessGreater;': '≶',
+	'LessLess;': '⪡',
+	'LessSlantEqual;': '⩽',
+	'LessTilde;': '≲',
+	'Lfr;': '𝔏',
+	'Ll;': '⋘',
+	'Lleftarrow;': '⇚',
+	'Lmidot;': 'Ŀ',
+	'LongLeftArrow;': '⟵',
+	'LongLeftRightArrow;': '⟷',
+	'LongRightArrow;': '⟶',
+	'Longleftarrow;': '⟸',
+	'Longleftrightarrow;': '⟺',
+	'Longrightarrow;': '⟹',
+	'Lopf;': '𝕃',
+	'LowerLeftArrow;': '↙',
+	'LowerRightArrow;': '↘',
+	'Lscr;': 'ℒ',
+	'Lsh;': '↰',
+	'Lstrok;': 'Ł',
+	'Lt;': '≪',
+	'Map;': '⤅',
+	'Mcy;': 'М',
+	'MediumSpace;': ' ',
+	'Mellintrf;': 'ℳ',
+	'Mfr;': '𝔐',
+	'MinusPlus;': '∓',
+	'Mopf;': '𝕄',
+	'Mscr;': 'ℳ',
+	'Mu;': 'Μ',
+	'NJcy;': 'Њ',
+	'Nacute;': 'Ń',
+	'Ncaron;': 'Ň',
+	'Ncedil;': 'Ņ',
+	'Ncy;': 'Н',
+	'NegativeMediumSpace;': '​',
+	'NegativeThickSpace;': '​',
+	'NegativeThinSpace;': '​',
+	'NegativeVeryThinSpace;': '​',
+	'NestedGreaterGreater;': '≫',
+	'NestedLessLess;': '≪',
+	'NewLine;': '\n',
+	'Nfr;': '𝔑',
+	'NoBreak;': '⁠',
+	'NonBreakingSpace;': ' ',
+	'Nopf;': 'ℕ',
+	'Not;': '⫬',
+	'NotCongruent;': '≢',
+	'NotCupCap;': '≭',
+	'NotDoubleVerticalBar;': '∦',
+	'NotElement;': '∉',
+	'NotEqual;': '≠',
+	'NotEqualTilde;': '≂̸',
+	'NotExists;': '∄',
+	'NotGreater;': '≯',
+	'NotGreaterEqual;': '≱',
+	'NotGreaterFullEqual;': '≧̸',
+	'NotGreaterGreater;': '≫̸',
+	'NotGreaterLess;': '≹',
+	'NotGreaterSlantEqual;': '⩾̸',
+	'NotGreaterTilde;': '≵',
+	'NotHumpDownHump;': '≎̸',
+	'NotHumpEqual;': '≏̸',
+	'NotLeftTriangle;': '⋪',
+	'NotLeftTriangleBar;': '⧏̸',
+	'NotLeftTriangleEqual;': '⋬',
+	'NotLess;': '≮',
+	'NotLessEqual;': '≰',
+	'NotLessGreater;': '≸',
+	'NotLessLess;': '≪̸',
+	'NotLessSlantEqual;': '⩽̸',
+	'NotLessTilde;': '≴',
+	'NotNestedGreaterGreater;': '⪢̸',
+	'NotNestedLessLess;': '⪡̸',
+	'NotPrecedes;': '⊀',
+	'NotPrecedesEqual;': '⪯̸',
+	'NotPrecedesSlantEqual;': '⋠',
+	'NotReverseElement;': '∌',
+	'NotRightTriangle;': '⋫',
+	'NotRightTriangleBar;': '⧐̸',
+	'NotRightTriangleEqual;': '⋭',
+	'NotSquareSubset;': '⊏̸',
+	'NotSquareSubsetEqual;': '⋢',
+	'NotSquareSuperset;': '⊐̸',
+	'NotSquareSupersetEqual;': '⋣',
+	'NotSubset;': '⊂⃒',
+	'NotSubsetEqual;': '⊈',
+	'NotSucceeds;': '⊁',
+	'NotSucceedsEqual;': '⪰̸',
+	'NotSucceedsSlantEqual;': '⋡',
+	'NotSucceedsTilde;': '≿̸',
+	'NotSuperset;': '⊃⃒',
+	'NotSupersetEqual;': '⊉',
+	'NotTilde;': '≁',
+	'NotTildeEqual;': '≄',
+	'NotTildeFullEqual;': '≇',
+	'NotTildeTilde;': '≉',
+	'NotVerticalBar;': '∤',
+	'Nscr;': '𝒩',
+	Ntilde: 'Ñ',
+	'Ntilde;': 'Ñ',
+	'Nu;': 'Ν',
+	'OElig;': 'Œ',
+	Oacute: 'Ó',
+	'Oacute;': 'Ó',
+	Ocirc: 'Ô',
+	'Ocirc;': 'Ô',
+	'Ocy;': 'О',
+	'Odblac;': 'Ő',
+	'Ofr;': '𝔒',
+	Ograve: 'Ò',
+	'Ograve;': 'Ò',
+	'Omacr;': 'Ō',
+	'Omega;': 'Ω',
+	'Omicron;': 'Ο',
+	'Oopf;': '𝕆',
+	'OpenCurlyDoubleQuote;': '“',
+	'OpenCurlyQuote;': '‘',
+	'Or;': '⩔',
+	'Oscr;': '𝒪',
+	Oslash: 'Ø',
+	'Oslash;': 'Ø',
+	Otilde: 'Õ',
+	'Otilde;': 'Õ',
+	'Otimes;': '⨷',
+	Ouml: 'Ö',
+	'Ouml;': 'Ö',
+	'OverBar;': '‾',
+	'OverBrace;': '⏞',
+	'OverBracket;': '⎴',
+	'OverParenthesis;': '⏜',
+	'PartialD;': '∂',
+	'Pcy;': 'П',
+	'Pfr;': '𝔓',
+	'Phi;': 'Φ',
+	'Pi;': 'Π',
+	'PlusMinus;': '±',
+	'Poincareplane;': 'ℌ',
+	'Popf;': 'ℙ',
+	'Pr;': '⪻',
+	'Precedes;': '≺',
+	'PrecedesEqual;': '⪯',
+	'PrecedesSlantEqual;': '≼',
+	'PrecedesTilde;': '≾',
+	'Prime;': '″',
+	'Product;': '∏',
+	'Proportion;': '∷',
+	'Proportional;': '∝',
+	'Pscr;': '𝒫',
+	'Psi;': 'Ψ',
+	QUOT: '"',
+	'QUOT;': '"',
+	'Qfr;': '𝔔',
+	'Qopf;': 'ℚ',
+	'Qscr;': '𝒬',
+	'RBarr;': '⤐',
+	REG: '®',
+	'REG;': '®',
+	'Racute;': 'Ŕ',
+	'Rang;': '⟫',
+	'Rarr;': '↠',
+	'Rarrtl;': '⤖',
+	'Rcaron;': 'Ř',
+	'Rcedil;': 'Ŗ',
+	'Rcy;': 'Р',
+	'Re;': 'ℜ',
+	'ReverseElement;': '∋',
+	'ReverseEquilibrium;': '⇋',
+	'ReverseUpEquilibrium;': '⥯',
+	'Rfr;': 'ℜ',
+	'Rho;': 'Ρ',
+	'RightAngleBracket;': '⟩',
+	'RightArrow;': '→',
+	'RightArrowBar;': '⇥',
+	'RightArrowLeftArrow;': '⇄',
+	'RightCeiling;': '⌉',
+	'RightDoubleBracket;': '⟧',
+	'RightDownTeeVector;': '⥝',
+	'RightDownVector;': '⇂',
+	'RightDownVectorBar;': '⥕',
+	'RightFloor;': '⌋',
+	'RightTee;': '⊢',
+	'RightTeeArrow;': '↦',
+	'RightTeeVector;': '⥛',
+	'RightTriangle;': '⊳',
+	'RightTriangleBar;': '⧐',
+	'RightTriangleEqual;': '⊵',
+	'RightUpDownVector;': '⥏',
+	'RightUpTeeVector;': '⥜',
+	'RightUpVector;': '↾',
+	'RightUpVectorBar;': '⥔',
+	'RightVector;': '⇀',
+	'RightVectorBar;': '⥓',
+	'Rightarrow;': '⇒',
+	'Ropf;': 'ℝ',
+	'RoundImplies;': '⥰',
+	'Rrightarrow;': '⇛',
+	'Rscr;': 'ℛ',
+	'Rsh;': '↱',
+	'RuleDelayed;': '⧴',
+	'SHCHcy;': 'Щ',
+	'SHcy;': 'Ш',
+	'SOFTcy;': 'Ь',
+	'Sacute;': 'Ś',
+	'Sc;': '⪼',
+	'Scaron;': 'Š',
+	'Scedil;': 'Ş',
+	'Scirc;': 'Ŝ',
+	'Scy;': 'С',
+	'Sfr;': '𝔖',
+	'ShortDownArrow;': '↓',
+	'ShortLeftArrow;': '←',
+	'ShortRightArrow;': '→',
+	'ShortUpArrow;': '↑',
+	'Sigma;': 'Σ',
+	'SmallCircle;': '∘',
+	'Sopf;': '𝕊',
+	'Sqrt;': '√',
+	'Square;': '□',
+	'SquareIntersection;': '⊓',
+	'SquareSubset;': '⊏',
+	'SquareSubsetEqual;': '⊑',
+	'SquareSuperset;': '⊐',
+	'SquareSupersetEqual;': '⊒',
+	'SquareUnion;': '⊔',
+	'Sscr;': '𝒮',
+	'Star;': '⋆',
+	'Sub;': '⋐',
+	'Subset;': '⋐',
+	'SubsetEqual;': '⊆',
+	'Succeeds;': '≻',
+	'SucceedsEqual;': '⪰',
+	'SucceedsSlantEqual;': '≽',
+	'SucceedsTilde;': '≿',
+	'SuchThat;': '∋',
+	'Sum;': '∑',
+	'Sup;': '⋑',
+	'Superset;': '⊃',
+	'SupersetEqual;': '⊇',
+	'Supset;': '⋑',
+	THORN: 'Þ',
+	'THORN;': 'Þ',
+	'TRADE;': '™',
+	'TSHcy;': 'Ћ',
+	'TScy;': 'Ц',
+	'Tab;': '\t',
+	'Tau;': 'Τ',
+	'Tcaron;': 'Ť',
+	'Tcedil;': 'Ţ',
+	'Tcy;': 'Т',
+	'Tfr;': '𝔗',
+	'Therefore;': '∴',
+	'Theta;': 'Θ',
+	'ThickSpace;': '  ',
+	'ThinSpace;': ' ',
+	'Tilde;': '∼',
+	'TildeEqual;': '≃',
+	'TildeFullEqual;': '≅',
+	'TildeTilde;': '≈',
+	'Topf;': '𝕋',
+	'TripleDot;': '⃛',
+	'Tscr;': '𝒯',
+	'Tstrok;': 'Ŧ',
+	Uacute: 'Ú',
+	'Uacute;': 'Ú',
+	'Uarr;': '↟',
+	'Uarrocir;': '⥉',
+	'Ubrcy;': 'Ў',
+	'Ubreve;': 'Ŭ',
+	Ucirc: 'Û',
+	'Ucirc;': 'Û',
+	'Ucy;': 'У',
+	'Udblac;': 'Ű',
+	'Ufr;': '𝔘',
+	Ugrave: 'Ù',
+	'Ugrave;': 'Ù',
+	'Umacr;': 'Ū',
+	'UnderBar;': '_',
+	'UnderBrace;': '⏟',
+	'UnderBracket;': '⎵',
+	'UnderParenthesis;': '⏝',
+	'Union;': '⋃',
+	'UnionPlus;': '⊎',
+	'Uogon;': 'Ų',
+	'Uopf;': '𝕌',
+	'UpArrow;': '↑',
+	'UpArrowBar;': '⤒',
+	'UpArrowDownArrow;': '⇅',
+	'UpDownArrow;': '↕',
+	'UpEquilibrium;': '⥮',
+	'UpTee;': '⊥',
+	'UpTeeArrow;': '↥',
+	'Uparrow;': '⇑',
+	'Updownarrow;': '⇕',
+	'UpperLeftArrow;': '↖',
+	'UpperRightArrow;': '↗',
+	'Upsi;': 'ϒ',
+	'Upsilon;': 'Υ',
+	'Uring;': 'Ů',
+	'Uscr;': '𝒰',
+	'Utilde;': 'Ũ',
+	Uuml: 'Ü',
+	'Uuml;': 'Ü',
+	'VDash;': '⊫',
+	'Vbar;': '⫫',
+	'Vcy;': 'В',
+	'Vdash;': '⊩',
+	'Vdashl;': '⫦',
+	'Vee;': '⋁',
+	'Verbar;': '‖',
+	'Vert;': '‖',
+	'VerticalBar;': '∣',
+	'VerticalLine;': '|',
+	'VerticalSeparator;': '❘',
+	'VerticalTilde;': '≀',
+	'VeryThinSpace;': ' ',
+	'Vfr;': '𝔙',
+	'Vopf;': '𝕍',
+	'Vscr;': '𝒱',
+	'Vvdash;': '⊪',
+	'Wcirc;': 'Ŵ',
+	'Wedge;': '⋀',
+	'Wfr;': '𝔚',
+	'Wopf;': '𝕎',
+	'Wscr;': '𝒲',
+	'Xfr;': '𝔛',
+	'Xi;': 'Ξ',
+	'Xopf;': '𝕏',
+	'Xscr;': '𝒳',
+	'YAcy;': 'Я',
+	'YIcy;': 'Ї',
+	'YUcy;': 'Ю',
+	Yacute: 'Ý',
+	'Yacute;': 'Ý',
+	'Ycirc;': 'Ŷ',
+	'Ycy;': 'Ы',
+	'Yfr;': '𝔜',
+	'Yopf;': '𝕐',
+	'Yscr;': '𝒴',
+	'Yuml;': 'Ÿ',
+	'ZHcy;': 'Ж',
+	'Zacute;': 'Ź',
+	'Zcaron;': 'Ž',
+	'Zcy;': 'З',
+	'Zdot;': 'Ż',
+	'ZeroWidthSpace;': '​',
+	'Zeta;': 'Ζ',
+	'Zfr;': 'ℨ',
+	'Zopf;': 'ℤ',
+	'Zscr;': '𝒵',
+	aacute: 'á',
+	'aacute;': 'á',
+	'abreve;': 'ă',
+	'ac;': '∾',
+	'acE;': '∾̳',
+	'acd;': '∿',
+	acirc: 'â',
+	'acirc;': 'â',
+	acute: '´',
+	'acute;': '´',
+	'acy;': 'а',
+	aelig: 'æ',
+	'aelig;': 'æ',
+	'af;': '⁡',
+	'afr;': '𝔞',
+	agrave: 'à',
+	'agrave;': 'à',
+	'alefsym;': 'ℵ',
+	'aleph;': 'ℵ',
+	'alpha;': 'α',
+	'amacr;': 'ā',
+	'amalg;': '⨿',
+	amp: '&',
+	'amp;': '&',
+	'and;': '∧',
+	'andand;': '⩕',
+	'andd;': '⩜',
+	'andslope;': '⩘',
+	'andv;': '⩚',
+	'ang;': '∠',
+	'ange;': '⦤',
+	'angle;': '∠',
+	'angmsd;': '∡',
+	'angmsdaa;': '⦨',
+	'angmsdab;': '⦩',
+	'angmsdac;': '⦪',
+	'angmsdad;': '⦫',
+	'angmsdae;': '⦬',
+	'angmsdaf;': '⦭',
+	'angmsdag;': '⦮',
+	'angmsdah;': '⦯',
+	'angrt;': '∟',
+	'angrtvb;': '⊾',
+	'angrtvbd;': '⦝',
+	'angsph;': '∢',
+	'angst;': 'Å',
+	'angzarr;': '⍼',
+	'aogon;': 'ą',
+	'aopf;': '𝕒',
+	'ap;': '≈',
+	'apE;': '⩰',
+	'apacir;': '⩯',
+	'ape;': '≊',
+	'apid;': '≋',
+	'apos;': "'",
+	'approx;': '≈',
+	'approxeq;': '≊',
+	aring: 'å',
+	'aring;': 'å',
+	'ascr;': '𝒶',
+	'ast;': '*',
+	'asymp;': '≈',
+	'asympeq;': '≍',
+	atilde: 'ã',
+	'atilde;': 'ã',
+	auml: 'ä',
+	'auml;': 'ä',
+	'awconint;': '∳',
+	'awint;': '⨑',
+	'bNot;': '⫭',
+	'backcong;': '≌',
+	'backepsilon;': '϶',
+	'backprime;': '‵',
+	'backsim;': '∽',
+	'backsimeq;': '⋍',
+	'barvee;': '⊽',
+	'barwed;': '⌅',
+	'barwedge;': '⌅',
+	'bbrk;': '⎵',
+	'bbrktbrk;': '⎶',
+	'bcong;': '≌',
+	'bcy;': 'б',
+	'bdquo;': '„',
+	'becaus;': '∵',
+	'because;': '∵',
+	'bemptyv;': '⦰',
+	'bepsi;': '϶',
+	'bernou;': 'ℬ',
+	'beta;': 'β',
+	'beth;': 'ℶ',
+	'between;': '≬',
+	'bfr;': '𝔟',
+	'bigcap;': '⋂',
+	'bigcirc;': '◯',
+	'bigcup;': '⋃',
+	'bigodot;': '⨀',
+	'bigoplus;': '⨁',
+	'bigotimes;': '⨂',
+	'bigsqcup;': '⨆',
+	'bigstar;': '★',
+	'bigtriangledown;': '▽',
+	'bigtriangleup;': '△',
+	'biguplus;': '⨄',
+	'bigvee;': '⋁',
+	'bigwedge;': '⋀',
+	'bkarow;': '⤍',
+	'blacklozenge;': '⧫',
+	'blacksquare;': '▪',
+	'blacktriangle;': '▴',
+	'blacktriangledown;': '▾',
+	'blacktriangleleft;': '◂',
+	'blacktriangleright;': '▸',
+	'blank;': '␣',
+	'blk12;': '▒',
+	'blk14;': '░',
+	'blk34;': '▓',
+	'block;': '█',
+	'bne;': '=⃥',
+	'bnequiv;': '≡⃥',
+	'bnot;': '⌐',
+	'bopf;': '𝕓',
+	'bot;': '⊥',
+	'bottom;': '⊥',
+	'bowtie;': '⋈',
+	'boxDL;': '╗',
+	'boxDR;': '╔',
+	'boxDl;': '╖',
+	'boxDr;': '╓',
+	'boxH;': '═',
+	'boxHD;': '╦',
+	'boxHU;': '╩',
+	'boxHd;': '╤',
+	'boxHu;': '╧',
+	'boxUL;': '╝',
+	'boxUR;': '╚',
+	'boxUl;': '╜',
+	'boxUr;': '╙',
+	'boxV;': '║',
+	'boxVH;': '╬',
+	'boxVL;': '╣',
+	'boxVR;': '╠',
+	'boxVh;': '╫',
+	'boxVl;': '╢',
+	'boxVr;': '╟',
+	'boxbox;': '⧉',
+	'boxdL;': '╕',
+	'boxdR;': '╒',
+	'boxdl;': '┐',
+	'boxdr;': '┌',
+	'boxh;': '─',
+	'boxhD;': '╥',
+	'boxhU;': '╨',
+	'boxhd;': '┬',
+	'boxhu;': '┴',
+	'boxminus;': '⊟',
+	'boxplus;': '⊞',
+	'boxtimes;': '⊠',
+	'boxuL;': '╛',
+	'boxuR;': '╘',
+	'boxul;': '┘',
+	'boxur;': '└',
+	'boxv;': '│',
+	'boxvH;': '╪',
+	'boxvL;': '╡',
+	'boxvR;': '╞',
+	'boxvh;': '┼',
+	'boxvl;': '┤',
+	'boxvr;': '├',
+	'bprime;': '‵',
+	'breve;': '˘',
+	brvbar: '¦',
+	'brvbar;': '¦',
+	'bscr;': '𝒷',
+	'bsemi;': '⁏',
+	'bsim;': '∽',
+	'bsime;': '⋍',
+	'bsol;': '\\',
+	'bsolb;': '⧅',
+	'bsolhsub;': '⟈',
+	'bull;': '•',
+	'bullet;': '•',
+	'bump;': '≎',
+	'bumpE;': '⪮',
+	'bumpe;': '≏',
+	'bumpeq;': '≏',
+	'cacute;': 'ć',
+	'cap;': '∩',
+	'capand;': '⩄',
+	'capbrcup;': '⩉',
+	'capcap;': '⩋',
+	'capcup;': '⩇',
+	'capdot;': '⩀',
+	'caps;': '∩︀',
+	'caret;': '⁁',
+	'caron;': 'ˇ',
+	'ccaps;': '⩍',
+	'ccaron;': 'č',
+	ccedil: 'ç',
+	'ccedil;': 'ç',
+	'ccirc;': 'ĉ',
+	'ccups;': '⩌',
+	'ccupssm;': '⩐',
+	'cdot;': 'ċ',
+	cedil: '¸',
+	'cedil;': '¸',
+	'cemptyv;': '⦲',
+	cent: '¢',
+	'cent;': '¢',
+	'centerdot;': '·',
+	'cfr;': '𝔠',
+	'chcy;': 'ч',
+	'check;': '✓',
+	'checkmark;': '✓',
+	'chi;': 'χ',
+	'cir;': '○',
+	'cirE;': '⧃',
+	'circ;': 'ˆ',
+	'circeq;': '≗',
+	'circlearrowleft;': '↺',
+	'circlearrowright;': '↻',
+	'circledR;': '®',
+	'circledS;': 'Ⓢ',
+	'circledast;': '⊛',
+	'circledcirc;': '⊚',
+	'circleddash;': '⊝',
+	'cire;': '≗',
+	'cirfnint;': '⨐',
+	'cirmid;': '⫯',
+	'cirscir;': '⧂',
+	'clubs;': '♣',
+	'clubsuit;': '♣',
+	'colon;': ':',
+	'colone;': '≔',
+	'coloneq;': '≔',
+	'comma;': ',',
+	'commat;': '@',
+	'comp;': '∁',
+	'compfn;': '∘',
+	'complement;': '∁',
+	'complexes;': 'ℂ',
+	'cong;': '≅',
+	'congdot;': '⩭',
+	'conint;': '∮',
+	'copf;': '𝕔',
+	'coprod;': '∐',
+	copy: '©',
+	'copy;': '©',
+	'copysr;': '℗',
+	'crarr;': '↵',
+	'cross;': '✗',
+	'cscr;': '𝒸',
+	'csub;': '⫏',
+	'csube;': '⫑',
+	'csup;': '⫐',
+	'csupe;': '⫒',
+	'ctdot;': '⋯',
+	'cudarrl;': '⤸',
+	'cudarrr;': '⤵',
+	'cuepr;': '⋞',
+	'cuesc;': '⋟',
+	'cularr;': '↶',
+	'cularrp;': '⤽',
+	'cup;': '∪',
+	'cupbrcap;': '⩈',
+	'cupcap;': '⩆',
+	'cupcup;': '⩊',
+	'cupdot;': '⊍',
+	'cupor;': '⩅',
+	'cups;': '∪︀',
+	'curarr;': '↷',
+	'curarrm;': '⤼',
+	'curlyeqprec;': '⋞',
+	'curlyeqsucc;': '⋟',
+	'curlyvee;': '⋎',
+	'curlywedge;': '⋏',
+	curren: '¤',
+	'curren;': '¤',
+	'curvearrowleft;': '↶',
+	'curvearrowright;': '↷',
+	'cuvee;': '⋎',
+	'cuwed;': '⋏',
+	'cwconint;': '∲',
+	'cwint;': '∱',
+	'cylcty;': '⌭',
+	'dArr;': '⇓',
+	'dHar;': '⥥',
+	'dagger;': '†',
+	'daleth;': 'ℸ',
+	'darr;': '↓',
+	'dash;': '‐',
+	'dashv;': '⊣',
+	'dbkarow;': '⤏',
+	'dblac;': '˝',
+	'dcaron;': 'ď',
+	'dcy;': 'д',
+	'dd;': 'ⅆ',
+	'ddagger;': '‡',
+	'ddarr;': '⇊',
+	'ddotseq;': '⩷',
+	deg: '°',
+	'deg;': '°',
+	'delta;': 'δ',
+	'demptyv;': '⦱',
+	'dfisht;': '⥿',
+	'dfr;': '𝔡',
+	'dharl;': '⇃',
+	'dharr;': '⇂',
+	'diam;': '⋄',
+	'diamond;': '⋄',
+	'diamondsuit;': '♦',
+	'diams;': '♦',
+	'die;': '¨',
+	'digamma;': 'ϝ',
+	'disin;': '⋲',
+	'div;': '÷',
+	divide: '÷',
+	'divide;': '÷',
+	'divideontimes;': '⋇',
+	'divonx;': '⋇',
+	'djcy;': 'ђ',
+	'dlcorn;': '⌞',
+	'dlcrop;': '⌍',
+	'dollar;': '$',
+	'dopf;': '𝕕',
+	'dot;': '˙',
+	'doteq;': '≐',
+	'doteqdot;': '≑',
+	'dotminus;': '∸',
+	'dotplus;': '∔',
+	'dotsquare;': '⊡',
+	'doublebarwedge;': '⌆',
+	'downarrow;': '↓',
+	'downdownarrows;': '⇊',
+	'downharpoonleft;': '⇃',
+	'downharpoonright;': '⇂',
+	'drbkarow;': '⤐',
+	'drcorn;': '⌟',
+	'drcrop;': '⌌',
+	'dscr;': '𝒹',
+	'dscy;': 'ѕ',
+	'dsol;': '⧶',
+	'dstrok;': 'đ',
+	'dtdot;': '⋱',
+	'dtri;': '▿',
+	'dtrif;': '▾',
+	'duarr;': '⇵',
+	'duhar;': '⥯',
+	'dwangle;': '⦦',
+	'dzcy;': 'џ',
+	'dzigrarr;': '⟿',
+	'eDDot;': '⩷',
+	'eDot;': '≑',
+	eacute: 'é',
+	'eacute;': 'é',
+	'easter;': '⩮',
+	'ecaron;': 'ě',
+	'ecir;': '≖',
+	ecirc: 'ê',
+	'ecirc;': 'ê',
+	'ecolon;': '≕',
+	'ecy;': 'э',
+	'edot;': 'ė',
+	'ee;': 'ⅇ',
+	'efDot;': '≒',
+	'efr;': '𝔢',
+	'eg;': '⪚',
+	egrave: 'è',
+	'egrave;': 'è',
+	'egs;': '⪖',
+	'egsdot;': '⪘',
+	'el;': '⪙',
+	'elinters;': '⏧',
+	'ell;': 'ℓ',
+	'els;': '⪕',
+	'elsdot;': '⪗',
+	'emacr;': 'ē',
+	'empty;': '∅',
+	'emptyset;': '∅',
+	'emptyv;': '∅',
+	'emsp13;': ' ',
+	'emsp14;': ' ',
+	'emsp;': ' ',
+	'eng;': 'ŋ',
+	'ensp;': ' ',
+	'eogon;': 'ę',
+	'eopf;': '𝕖',
+	'epar;': '⋕',
+	'eparsl;': '⧣',
+	'eplus;': '⩱',
+	'epsi;': 'ε',
+	'epsilon;': 'ε',
+	'epsiv;': 'ϵ',
+	'eqcirc;': '≖',
+	'eqcolon;': '≕',
+	'eqsim;': '≂',
+	'eqslantgtr;': '⪖',
+	'eqslantless;': '⪕',
+	'equals;': '=',
+	'equest;': '≟',
+	'equiv;': '≡',
+	'equivDD;': '⩸',
+	'eqvparsl;': '⧥',
+	'erDot;': '≓',
+	'erarr;': '⥱',
+	'escr;': 'ℯ',
+	'esdot;': '≐',
+	'esim;': '≂',
+	'eta;': 'η',
+	eth: 'ð',
+	'eth;': 'ð',
+	euml: 'ë',
+	'euml;': 'ë',
+	'euro;': '€',
+	'excl;': '!',
+	'exist;': '∃',
+	'expectation;': 'ℰ',
+	'exponentiale;': 'ⅇ',
+	'fallingdotseq;': '≒',
+	'fcy;': 'ф',
+	'female;': '♀',
+	'ffilig;': 'ﬃ',
+	'fflig;': 'ﬀ',
+	'ffllig;': 'ﬄ',
+	'ffr;': '𝔣',
+	'filig;': 'ﬁ',
+	'fjlig;': 'fj',
+	'flat;': '♭',
+	'fllig;': 'ﬂ',
+	'fltns;': '▱',
+	'fnof;': 'ƒ',
+	'fopf;': '𝕗',
+	'forall;': '∀',
+	'fork;': '⋔',
+	'forkv;': '⫙',
+	'fpartint;': '⨍',
+	frac12: '½',
+	'frac12;': '½',
+	'frac13;': '⅓',
+	frac14: '¼',
+	'frac14;': '¼',
+	'frac15;': '⅕',
+	'frac16;': '⅙',
+	'frac18;': '⅛',
+	'frac23;': '⅔',
+	'frac25;': '⅖',
+	frac34: '¾',
+	'frac34;': '¾',
+	'frac35;': '⅗',
+	'frac38;': '⅜',
+	'frac45;': '⅘',
+	'frac56;': '⅚',
+	'frac58;': '⅝',
+	'frac78;': '⅞',
+	'frasl;': '⁄',
+	'frown;': '⌢',
+	'fscr;': '𝒻',
+	'gE;': '≧',
+	'gEl;': '⪌',
+	'gacute;': 'ǵ',
+	'gamma;': 'γ',
+	'gammad;': 'ϝ',
+	'gap;': '⪆',
+	'gbreve;': 'ğ',
+	'gcirc;': 'ĝ',
+	'gcy;': 'г',
+	'gdot;': 'ġ',
+	'ge;': '≥',
+	'gel;': '⋛',
+	'geq;': '≥',
+	'geqq;': '≧',
+	'geqslant;': '⩾',
+	'ges;': '⩾',
+	'gescc;': '⪩',
+	'gesdot;': '⪀',
+	'gesdoto;': '⪂',
+	'gesdotol;': '⪄',
+	'gesl;': '⋛︀',
+	'gesles;': '⪔',
+	'gfr;': '𝔤',
+	'gg;': '≫',
+	'ggg;': '⋙',
+	'gimel;': 'ℷ',
+	'gjcy;': 'ѓ',
+	'gl;': '≷',
+	'glE;': '⪒',
+	'gla;': '⪥',
+	'glj;': '⪤',
+	'gnE;': '≩',
+	'gnap;': '⪊',
+	'gnapprox;': '⪊',
+	'gne;': '⪈',
+	'gneq;': '⪈',
+	'gneqq;': '≩',
+	'gnsim;': '⋧',
+	'gopf;': '𝕘',
+	'grave;': '`',
+	'gscr;': 'ℊ',
+	'gsim;': '≳',
+	'gsime;': '⪎',
+	'gsiml;': '⪐',
+	gt: '>',
+	'gt;': '>',
+	'gtcc;': '⪧',
+	'gtcir;': '⩺',
+	'gtdot;': '⋗',
+	'gtlPar;': '⦕',
+	'gtquest;': '⩼',
+	'gtrapprox;': '⪆',
+	'gtrarr;': '⥸',
+	'gtrdot;': '⋗',
+	'gtreqless;': '⋛',
+	'gtreqqless;': '⪌',
+	'gtrless;': '≷',
+	'gtrsim;': '≳',
+	'gvertneqq;': '≩︀',
+	'gvnE;': '≩︀',
+	'hArr;': '⇔',
+	'hairsp;': ' ',
+	'half;': '½',
+	'hamilt;': 'ℋ',
+	'hardcy;': 'ъ',
+	'harr;': '↔',
+	'harrcir;': '⥈',
+	'harrw;': '↭',
+	'hbar;': 'ℏ',
+	'hcirc;': 'ĥ',
+	'hearts;': '♥',
+	'heartsuit;': '♥',
+	'hellip;': '…',
+	'hercon;': '⊹',
+	'hfr;': '𝔥',
+	'hksearow;': '⤥',
+	'hkswarow;': '⤦',
+	'hoarr;': '⇿',
+	'homtht;': '∻',
+	'hookleftarrow;': '↩',
+	'hookrightarrow;': '↪',
+	'hopf;': '𝕙',
+	'horbar;': '―',
+	'hscr;': '𝒽',
+	'hslash;': 'ℏ',
+	'hstrok;': 'ħ',
+	'hybull;': '⁃',
+	'hyphen;': '‐',
+	iacute: 'í',
+	'iacute;': 'í',
+	'ic;': '⁣',
+	icirc: 'î',
+	'icirc;': 'î',
+	'icy;': 'и',
+	'iecy;': 'е',
+	iexcl: '¡',
+	'iexcl;': '¡',
+	'iff;': '⇔',
+	'ifr;': '𝔦',
+	igrave: 'ì',
+	'igrave;': 'ì',
+	'ii;': 'ⅈ',
+	'iiiint;': '⨌',
+	'iiint;': '∭',
+	'iinfin;': '⧜',
+	'iiota;': '℩',
+	'ijlig;': 'ĳ',
+	'imacr;': 'ī',
+	'image;': 'ℑ',
+	'imagline;': 'ℐ',
+	'imagpart;': 'ℑ',
+	'imath;': 'ı',
+	'imof;': '⊷',
+	'imped;': 'Ƶ',
+	'in;': '∈',
+	'incare;': '℅',
+	'infin;': '∞',
+	'infintie;': '⧝',
+	'inodot;': 'ı',
+	'int;': '∫',
+	'intcal;': '⊺',
+	'integers;': 'ℤ',
+	'intercal;': '⊺',
+	'intlarhk;': '⨗',
+	'intprod;': '⨼',
+	'iocy;': 'ё',
+	'iogon;': 'į',
+	'iopf;': '𝕚',
+	'iota;': 'ι',
+	'iprod;': '⨼',
+	iquest: '¿',
+	'iquest;': '¿',
+	'iscr;': '𝒾',
+	'isin;': '∈',
+	'isinE;': '⋹',
+	'isindot;': '⋵',
+	'isins;': '⋴',
+	'isinsv;': '⋳',
+	'isinv;': '∈',
+	'it;': '⁢',
+	'itilde;': 'ĩ',
+	'iukcy;': 'і',
+	iuml: 'ï',
+	'iuml;': 'ï',
+	'jcirc;': 'ĵ',
+	'jcy;': 'й',
+	'jfr;': '𝔧',
+	'jmath;': 'ȷ',
+	'jopf;': '𝕛',
+	'jscr;': '𝒿',
+	'jsercy;': 'ј',
+	'jukcy;': 'є',
+	'kappa;': 'κ',
+	'kappav;': 'ϰ',
+	'kcedil;': 'ķ',
+	'kcy;': 'к',
+	'kfr;': '𝔨',
+	'kgreen;': 'ĸ',
+	'khcy;': 'х',
+	'kjcy;': 'ќ',
+	'kopf;': '𝕜',
+	'kscr;': '𝓀',
+	'lAarr;': '⇚',
+	'lArr;': '⇐',
+	'lAtail;': '⤛',
+	'lBarr;': '⤎',
+	'lE;': '≦',
+	'lEg;': '⪋',
+	'lHar;': '⥢',
+	'lacute;': 'ĺ',
+	'laemptyv;': '⦴',
+	'lagran;': 'ℒ',
+	'lambda;': 'λ',
+	'lang;': '⟨',
+	'langd;': '⦑',
+	'langle;': '⟨',
+	'lap;': '⪅',
+	laquo: '«',
+	'laquo;': '«',
+	'larr;': '←',
+	'larrb;': '⇤',
+	'larrbfs;': '⤟',
+	'larrfs;': '⤝',
+	'larrhk;': '↩',
+	'larrlp;': '↫',
+	'larrpl;': '⤹',
+	'larrsim;': '⥳',
+	'larrtl;': '↢',
+	'lat;': '⪫',
+	'latail;': '⤙',
+	'late;': '⪭',
+	'lates;': '⪭︀',
+	'lbarr;': '⤌',
+	'lbbrk;': '❲',
+	'lbrace;': '{',
+	'lbrack;': '[',
+	'lbrke;': '⦋',
+	'lbrksld;': '⦏',
+	'lbrkslu;': '⦍',
+	'lcaron;': 'ľ',
+	'lcedil;': 'ļ',
+	'lceil;': '⌈',
+	'lcub;': '{',
+	'lcy;': 'л',
+	'ldca;': '⤶',
+	'ldquo;': '“',
+	'ldquor;': '„',
+	'ldrdhar;': '⥧',
+	'ldrushar;': '⥋',
+	'ldsh;': '↲',
+	'le;': '≤',
+	'leftarrow;': '←',
+	'leftarrowtail;': '↢',
+	'leftharpoondown;': '↽',
+	'leftharpoonup;': '↼',
+	'leftleftarrows;': '⇇',
+	'leftrightarrow;': '↔',
+	'leftrightarrows;': '⇆',
+	'leftrightharpoons;': '⇋',
+	'leftrightsquigarrow;': '↭',
+	'leftthreetimes;': '⋋',
+	'leg;': '⋚',
+	'leq;': '≤',
+	'leqq;': '≦',
+	'leqslant;': '⩽',
+	'les;': '⩽',
+	'lescc;': '⪨',
+	'lesdot;': '⩿',
+	'lesdoto;': '⪁',
+	'lesdotor;': '⪃',
+	'lesg;': '⋚︀',
+	'lesges;': '⪓',
+	'lessapprox;': '⪅',
+	'lessdot;': '⋖',
+	'lesseqgtr;': '⋚',
+	'lesseqqgtr;': '⪋',
+	'lessgtr;': '≶',
+	'lesssim;': '≲',
+	'lfisht;': '⥼',
+	'lfloor;': '⌊',
+	'lfr;': '𝔩',
+	'lg;': '≶',
+	'lgE;': '⪑',
+	'lhard;': '↽',
+	'lharu;': '↼',
+	'lharul;': '⥪',
+	'lhblk;': '▄',
+	'ljcy;': 'љ',
+	'll;': '≪',
+	'llarr;': '⇇',
+	'llcorner;': '⌞',
+	'llhard;': '⥫',
+	'lltri;': '◺',
+	'lmidot;': 'ŀ',
+	'lmoust;': '⎰',
+	'lmoustache;': '⎰',
+	'lnE;': '≨',
+	'lnap;': '⪉',
+	'lnapprox;': '⪉',
+	'lne;': '⪇',
+	'lneq;': '⪇',
+	'lneqq;': '≨',
+	'lnsim;': '⋦',
+	'loang;': '⟬',
+	'loarr;': '⇽',
+	'lobrk;': '⟦',
+	'longleftarrow;': '⟵',
+	'longleftrightarrow;': '⟷',
+	'longmapsto;': '⟼',
+	'longrightarrow;': '⟶',
+	'looparrowleft;': '↫',
+	'looparrowright;': '↬',
+	'lopar;': '⦅',
+	'lopf;': '𝕝',
+	'loplus;': '⨭',
+	'lotimes;': '⨴',
+	'lowast;': '∗',
+	'lowbar;': '_',
+	'loz;': '◊',
+	'lozenge;': '◊',
+	'lozf;': '⧫',
+	'lpar;': '(',
+	'lparlt;': '⦓',
+	'lrarr;': '⇆',
+	'lrcorner;': '⌟',
+	'lrhar;': '⇋',
+	'lrhard;': '⥭',
+	'lrm;': '‎',
+	'lrtri;': '⊿',
+	'lsaquo;': '‹',
+	'lscr;': '𝓁',
+	'lsh;': '↰',
+	'lsim;': '≲',
+	'lsime;': '⪍',
+	'lsimg;': '⪏',
+	'lsqb;': '[',
+	'lsquo;': '‘',
+	'lsquor;': '‚',
+	'lstrok;': 'ł',
+	lt: '<',
+	'lt;': '<',
+	'ltcc;': '⪦',
+	'ltcir;': '⩹',
+	'ltdot;': '⋖',
+	'lthree;': '⋋',
+	'ltimes;': '⋉',
+	'ltlarr;': '⥶',
+	'ltquest;': '⩻',
+	'ltrPar;': '⦖',
+	'ltri;': '◃',
+	'ltrie;': '⊴',
+	'ltrif;': '◂',
+	'lurdshar;': '⥊',
+	'luruhar;': '⥦',
+	'lvertneqq;': '≨︀',
+	'lvnE;': '≨︀',
+	'mDDot;': '∺',
+	macr: '¯',
+	'macr;': '¯',
+	'male;': '♂',
+	'malt;': '✠',
+	'maltese;': '✠',
+	'map;': '↦',
+	'mapsto;': '↦',
+	'mapstodown;': '↧',
+	'mapstoleft;': '↤',
+	'mapstoup;': '↥',
+	'marker;': '▮',
+	'mcomma;': '⨩',
+	'mcy;': 'м',
+	'mdash;': '—',
+	'measuredangle;': '∡',
+	'mfr;': '𝔪',
+	'mho;': '℧',
+	micro: 'µ',
+	'micro;': 'µ',
+	'mid;': '∣',
+	'midast;': '*',
+	'midcir;': '⫰',
+	middot: '·',
+	'middot;': '·',
+	'minus;': '−',
+	'minusb;': '⊟',
+	'minusd;': '∸',
+	'minusdu;': '⨪',
+	'mlcp;': '⫛',
+	'mldr;': '…',
+	'mnplus;': '∓',
+	'models;': '⊧',
+	'mopf;': '𝕞',
+	'mp;': '∓',
+	'mscr;': '𝓂',
+	'mstpos;': '∾',
+	'mu;': 'μ',
+	'multimap;': '⊸',
+	'mumap;': '⊸',
+	'nGg;': '⋙̸',
+	'nGt;': '≫⃒',
+	'nGtv;': '≫̸',
+	'nLeftarrow;': '⇍',
+	'nLeftrightarrow;': '⇎',
+	'nLl;': '⋘̸',
+	'nLt;': '≪⃒',
+	'nLtv;': '≪̸',
+	'nRightarrow;': '⇏',
+	'nVDash;': '⊯',
+	'nVdash;': '⊮',
+	'nabla;': '∇',
+	'nacute;': 'ń',
+	'nang;': '∠⃒',
+	'nap;': '≉',
+	'napE;': '⩰̸',
+	'napid;': '≋̸',
+	'napos;': 'ŉ',
+	'napprox;': '≉',
+	'natur;': '♮',
+	'natural;': '♮',
+	'naturals;': 'ℕ',
+	nbsp: ' ',
+	'nbsp;': ' ',
+	'nbump;': '≎̸',
+	'nbumpe;': '≏̸',
+	'ncap;': '⩃',
+	'ncaron;': 'ň',
+	'ncedil;': 'ņ',
+	'ncong;': '≇',
+	'ncongdot;': '⩭̸',
+	'ncup;': '⩂',
+	'ncy;': 'н',
+	'ndash;': '–',
+	'ne;': '≠',
+	'neArr;': '⇗',
+	'nearhk;': '⤤',
+	'nearr;': '↗',
+	'nearrow;': '↗',
+	'nedot;': '≐̸',
+	'nequiv;': '≢',
+	'nesear;': '⤨',
+	'nesim;': '≂̸',
+	'nexist;': '∄',
+	'nexists;': '∄',
+	'nfr;': '𝔫',
+	'ngE;': '≧̸',
+	'nge;': '≱',
+	'ngeq;': '≱',
+	'ngeqq;': '≧̸',
+	'ngeqslant;': '⩾̸',
+	'nges;': '⩾̸',
+	'ngsim;': '≵',
+	'ngt;': '≯',
+	'ngtr;': '≯',
+	'nhArr;': '⇎',
+	'nharr;': '↮',
+	'nhpar;': '⫲',
+	'ni;': '∋',
+	'nis;': '⋼',
+	'nisd;': '⋺',
+	'niv;': '∋',
+	'njcy;': 'њ',
+	'nlArr;': '⇍',
+	'nlE;': '≦̸',
+	'nlarr;': '↚',
+	'nldr;': '‥',
+	'nle;': '≰',
+	'nleftarrow;': '↚',
+	'nleftrightarrow;': '↮',
+	'nleq;': '≰',
+	'nleqq;': '≦̸',
+	'nleqslant;': '⩽̸',
+	'nles;': '⩽̸',
+	'nless;': '≮',
+	'nlsim;': '≴',
+	'nlt;': '≮',
+	'nltri;': '⋪',
+	'nltrie;': '⋬',
+	'nmid;': '∤',
+	'nopf;': '𝕟',
+	not: '¬',
+	'not;': '¬',
+	'notin;': '∉',
+	'notinE;': '⋹̸',
+	'notindot;': '⋵̸',
+	'notinva;': '∉',
+	'notinvb;': '⋷',
+	'notinvc;': '⋶',
+	'notni;': '∌',
+	'notniva;': '∌',
+	'notnivb;': '⋾',
+	'notnivc;': '⋽',
+	'npar;': '∦',
+	'nparallel;': '∦',
+	'nparsl;': '⫽⃥',
+	'npart;': '∂̸',
+	'npolint;': '⨔',
+	'npr;': '⊀',
+	'nprcue;': '⋠',
+	'npre;': '⪯̸',
+	'nprec;': '⊀',
+	'npreceq;': '⪯̸',
+	'nrArr;': '⇏',
+	'nrarr;': '↛',
+	'nrarrc;': '⤳̸',
+	'nrarrw;': '↝̸',
+	'nrightarrow;': '↛',
+	'nrtri;': '⋫',
+	'nrtrie;': '⋭',
+	'nsc;': '⊁',
+	'nsccue;': '⋡',
+	'nsce;': '⪰̸',
+	'nscr;': '𝓃',
+	'nshortmid;': '∤',
+	'nshortparallel;': '∦',
+	'nsim;': '≁',
+	'nsime;': '≄',
+	'nsimeq;': '≄',
+	'nsmid;': '∤',
+	'nspar;': '∦',
+	'nsqsube;': '⋢',
+	'nsqsupe;': '⋣',
+	'nsub;': '⊄',
+	'nsubE;': '⫅̸',
+	'nsube;': '⊈',
+	'nsubset;': '⊂⃒',
+	'nsubseteq;': '⊈',
+	'nsubseteqq;': '⫅̸',
+	'nsucc;': '⊁',
+	'nsucceq;': '⪰̸',
+	'nsup;': '⊅',
+	'nsupE;': '⫆̸',
+	'nsupe;': '⊉',
+	'nsupset;': '⊃⃒',
+	'nsupseteq;': '⊉',
+	'nsupseteqq;': '⫆̸',
+	'ntgl;': '≹',
+	ntilde: 'ñ',
+	'ntilde;': 'ñ',
+	'ntlg;': '≸',
+	'ntriangleleft;': '⋪',
+	'ntrianglelefteq;': '⋬',
+	'ntriangleright;': '⋫',
+	'ntrianglerighteq;': '⋭',
+	'nu;': 'ν',
+	'num;': '#',
+	'numero;': '№',
+	'numsp;': ' ',
+	'nvDash;': '⊭',
+	'nvHarr;': '⤄',
+	'nvap;': '≍⃒',
+	'nvdash;': '⊬',
+	'nvge;': '≥⃒',
+	'nvgt;': '>⃒',
+	'nvinfin;': '⧞',
+	'nvlArr;': '⤂',
+	'nvle;': '≤⃒',
+	'nvlt;': '<⃒',
+	'nvltrie;': '⊴⃒',
+	'nvrArr;': '⤃',
+	'nvrtrie;': '⊵⃒',
+	'nvsim;': '∼⃒',
+	'nwArr;': '⇖',
+	'nwarhk;': '⤣',
+	'nwarr;': '↖',
+	'nwarrow;': '↖',
+	'nwnear;': '⤧',
+	'oS;': 'Ⓢ',
+	oacute: 'ó',
+	'oacute;': 'ó',
+	'oast;': '⊛',
+	'ocir;': '⊚',
+	ocirc: 'ô',
+	'ocirc;': 'ô',
+	'ocy;': 'о',
+	'odash;': '⊝',
+	'odblac;': 'ő',
+	'odiv;': '⨸',
+	'odot;': '⊙',
+	'odsold;': '⦼',
+	'oelig;': 'œ',
+	'ofcir;': '⦿',
+	'ofr;': '𝔬',
+	'ogon;': '˛',
+	ograve: 'ò',
+	'ograve;': 'ò',
+	'ogt;': '⧁',
+	'ohbar;': '⦵',
+	'ohm;': 'Ω',
+	'oint;': '∮',
+	'olarr;': '↺',
+	'olcir;': '⦾',
+	'olcross;': '⦻',
+	'oline;': '‾',
+	'olt;': '⧀',
+	'omacr;': 'ō',
+	'omega;': 'ω',
+	'omicron;': 'ο',
+	'omid;': '⦶',
+	'ominus;': '⊖',
+	'oopf;': '𝕠',
+	'opar;': '⦷',
+	'operp;': '⦹',
+	'oplus;': '⊕',
+	'or;': '∨',
+	'orarr;': '↻',
+	'ord;': '⩝',
+	'order;': 'ℴ',
+	'orderof;': 'ℴ',
+	ordf: 'ª',
+	'ordf;': 'ª',
+	ordm: 'º',
+	'ordm;': 'º',
+	'origof;': '⊶',
+	'oror;': '⩖',
+	'orslope;': '⩗',
+	'orv;': '⩛',
+	'oscr;': 'ℴ',
+	oslash: 'ø',
+	'oslash;': 'ø',
+	'osol;': '⊘',
+	otilde: 'õ',
+	'otilde;': 'õ',
+	'otimes;': '⊗',
+	'otimesas;': '⨶',
+	ouml: 'ö',
+	'ouml;': 'ö',
+	'ovbar;': '⌽',
+	'par;': '∥',
+	para: '¶',
+	'para;': '¶',
+	'parallel;': '∥',
+	'parsim;': '⫳',
+	'parsl;': '⫽',
+	'part;': '∂',
+	'pcy;': 'п',
+	'percnt;': '%',
+	'period;': '.',
+	'permil;': '‰',
+	'perp;': '⊥',
+	'pertenk;': '‱',
+	'pfr;': '𝔭',
+	'phi;': 'φ',
+	'phiv;': 'ϕ',
+	'phmmat;': 'ℳ',
+	'phone;': '☎',
+	'pi;': 'π',
+	'pitchfork;': '⋔',
+	'piv;': 'ϖ',
+	'planck;': 'ℏ',
+	'planckh;': 'ℎ',
+	'plankv;': 'ℏ',
+	'plus;': '+',
+	'plusacir;': '⨣',
+	'plusb;': '⊞',
+	'pluscir;': '⨢',
+	'plusdo;': '∔',
+	'plusdu;': '⨥',
+	'pluse;': '⩲',
+	plusmn: '±',
+	'plusmn;': '±',
+	'plussim;': '⨦',
+	'plustwo;': '⨧',
+	'pm;': '±',
+	'pointint;': '⨕',
+	'popf;': '𝕡',
+	pound: '£',
+	'pound;': '£',
+	'pr;': '≺',
+	'prE;': '⪳',
+	'prap;': '⪷',
+	'prcue;': '≼',
+	'pre;': '⪯',
+	'prec;': '≺',
+	'precapprox;': '⪷',
+	'preccurlyeq;': '≼',
+	'preceq;': '⪯',
+	'precnapprox;': '⪹',
+	'precneqq;': '⪵',
+	'precnsim;': '⋨',
+	'precsim;': '≾',
+	'prime;': '′',
+	'primes;': 'ℙ',
+	'prnE;': '⪵',
+	'prnap;': '⪹',
+	'prnsim;': '⋨',
+	'prod;': '∏',
+	'profalar;': '⌮',
+	'profline;': '⌒',
+	'profsurf;': '⌓',
+	'prop;': '∝',
+	'propto;': '∝',
+	'prsim;': '≾',
+	'prurel;': '⊰',
+	'pscr;': '𝓅',
+	'psi;': 'ψ',
+	'puncsp;': ' ',
+	'qfr;': '𝔮',
+	'qint;': '⨌',
+	'qopf;': '𝕢',
+	'qprime;': '⁗',
+	'qscr;': '𝓆',
+	'quaternions;': 'ℍ',
+	'quatint;': '⨖',
+	'quest;': '?',
+	'questeq;': '≟',
+	quot: '"',
+	'quot;': '"',
+	'rAarr;': '⇛',
+	'rArr;': '⇒',
+	'rAtail;': '⤜',
+	'rBarr;': '⤏',
+	'rHar;': '⥤',
+	'race;': '∽̱',
+	'racute;': 'ŕ',
+	'radic;': '√',
+	'raemptyv;': '⦳',
+	'rang;': '⟩',
+	'rangd;': '⦒',
+	'range;': '⦥',
+	'rangle;': '⟩',
+	raquo: '»',
+	'raquo;': '»',
+	'rarr;': '→',
+	'rarrap;': '⥵',
+	'rarrb;': '⇥',
+	'rarrbfs;': '⤠',
+	'rarrc;': '⤳',
+	'rarrfs;': '⤞',
+	'rarrhk;': '↪',
+	'rarrlp;': '↬',
+	'rarrpl;': '⥅',
+	'rarrsim;': '⥴',
+	'rarrtl;': '↣',
+	'rarrw;': '↝',
+	'ratail;': '⤚',
+	'ratio;': '∶',
+	'rationals;': 'ℚ',
+	'rbarr;': '⤍',
+	'rbbrk;': '❳',
+	'rbrace;': '}',
+	'rbrack;': ']',
+	'rbrke;': '⦌',
+	'rbrksld;': '⦎',
+	'rbrkslu;': '⦐',
+	'rcaron;': 'ř',
+	'rcedil;': 'ŗ',
+	'rceil;': '⌉',
+	'rcub;': '}',
+	'rcy;': 'р',
+	'rdca;': '⤷',
+	'rdldhar;': '⥩',
+	'rdquo;': '”',
+	'rdquor;': '”',
+	'rdsh;': '↳',
+	'real;': 'ℜ',
+	'realine;': 'ℛ',
+	'realpart;': 'ℜ',
+	'reals;': 'ℝ',
+	'rect;': '▭',
+	reg: '®',
+	'reg;': '®',
+	'rfisht;': '⥽',
+	'rfloor;': '⌋',
+	'rfr;': '𝔯',
+	'rhard;': '⇁',
+	'rharu;': '⇀',
+	'rharul;': '⥬',
+	'rho;': 'ρ',
+	'rhov;': 'ϱ',
+	'rightarrow;': '→',
+	'rightarrowtail;': '↣',
+	'rightharpoondown;': '⇁',
+	'rightharpoonup;': '⇀',
+	'rightleftarrows;': '⇄',
+	'rightleftharpoons;': '⇌',
+	'rightrightarrows;': '⇉',
+	'rightsquigarrow;': '↝',
+	'rightthreetimes;': '⋌',
+	'ring;': '˚',
+	'risingdotseq;': '≓',
+	'rlarr;': '⇄',
+	'rlhar;': '⇌',
+	'rlm;': '‏',
+	'rmoust;': '⎱',
+	'rmoustache;': '⎱',
+	'rnmid;': '⫮',
+	'roang;': '⟭',
+	'roarr;': '⇾',
+	'robrk;': '⟧',
+	'ropar;': '⦆',
+	'ropf;': '𝕣',
+	'roplus;': '⨮',
+	'rotimes;': '⨵',
+	'rpar;': ')',
+	'rpargt;': '⦔',
+	'rppolint;': '⨒',
+	'rrarr;': '⇉',
+	'rsaquo;': '›',
+	'rscr;': '𝓇',
+	'rsh;': '↱',
+	'rsqb;': ']',
+	'rsquo;': '’',
+	'rsquor;': '’',
+	'rthree;': '⋌',
+	'rtimes;': '⋊',
+	'rtri;': '▹',
+	'rtrie;': '⊵',
+	'rtrif;': '▸',
+	'rtriltri;': '⧎',
+	'ruluhar;': '⥨',
+	'rx;': '℞',
+	'sacute;': 'ś',
+	'sbquo;': '‚',
+	'sc;': '≻',
+	'scE;': '⪴',
+	'scap;': '⪸',
+	'scaron;': 'š',
+	'sccue;': '≽',
+	'sce;': '⪰',
+	'scedil;': 'ş',
+	'scirc;': 'ŝ',
+	'scnE;': '⪶',
+	'scnap;': '⪺',
+	'scnsim;': '⋩',
+	'scpolint;': '⨓',
+	'scsim;': '≿',
+	'scy;': 'с',
+	'sdot;': '⋅',
+	'sdotb;': '⊡',
+	'sdote;': '⩦',
+	'seArr;': '⇘',
+	'searhk;': '⤥',
+	'searr;': '↘',
+	'searrow;': '↘',
+	sect: '§',
+	'sect;': '§',
+	'semi;': ';',
+	'seswar;': '⤩',
+	'setminus;': '∖',
+	'setmn;': '∖',
+	'sext;': '✶',
+	'sfr;': '𝔰',
+	'sfrown;': '⌢',
+	'sharp;': '♯',
+	'shchcy;': 'щ',
+	'shcy;': 'ш',
+	'shortmid;': '∣',
+	'shortparallel;': '∥',
+	shy: '­',
+	'shy;': '­',
+	'sigma;': 'σ',
+	'sigmaf;': 'ς',
+	'sigmav;': 'ς',
+	'sim;': '∼',
+	'simdot;': '⩪',
+	'sime;': '≃',
+	'simeq;': '≃',
+	'simg;': '⪞',
+	'simgE;': '⪠',
+	'siml;': '⪝',
+	'simlE;': '⪟',
+	'simne;': '≆',
+	'simplus;': '⨤',
+	'simrarr;': '⥲',
+	'slarr;': '←',
+	'smallsetminus;': '∖',
+	'smashp;': '⨳',
+	'smeparsl;': '⧤',
+	'smid;': '∣',
+	'smile;': '⌣',
+	'smt;': '⪪',
+	'smte;': '⪬',
+	'smtes;': '⪬︀',
+	'softcy;': 'ь',
+	'sol;': '/',
+	'solb;': '⧄',
+	'solbar;': '⌿',
+	'sopf;': '𝕤',
+	'spades;': '♠',
+	'spadesuit;': '♠',
+	'spar;': '∥',
+	'sqcap;': '⊓',
+	'sqcaps;': '⊓︀',
+	'sqcup;': '⊔',
+	'sqcups;': '⊔︀',
+	'sqsub;': '⊏',
+	'sqsube;': '⊑',
+	'sqsubset;': '⊏',
+	'sqsubseteq;': '⊑',
+	'sqsup;': '⊐',
+	'sqsupe;': '⊒',
+	'sqsupset;': '⊐',
+	'sqsupseteq;': '⊒',
+	'squ;': '□',
+	'square;': '□',
+	'squarf;': '▪',
+	'squf;': '▪',
+	'srarr;': '→',
+	'sscr;': '𝓈',
+	'ssetmn;': '∖',
+	'ssmile;': '⌣',
+	'sstarf;': '⋆',
+	'star;': '☆',
+	'starf;': '★',
+	'straightepsilon;': 'ϵ',
+	'straightphi;': 'ϕ',
+	'strns;': '¯',
+	'sub;': '⊂',
+	'subE;': '⫅',
+	'subdot;': '⪽',
+	'sube;': '⊆',
+	'subedot;': '⫃',
+	'submult;': '⫁',
+	'subnE;': '⫋',
+	'subne;': '⊊',
+	'subplus;': '⪿',
+	'subrarr;': '⥹',
+	'subset;': '⊂',
+	'subseteq;': '⊆',
+	'subseteqq;': '⫅',
+	'subsetneq;': '⊊',
+	'subsetneqq;': '⫋',
+	'subsim;': '⫇',
+	'subsub;': '⫕',
+	'subsup;': '⫓',
+	'succ;': '≻',
+	'succapprox;': '⪸',
+	'succcurlyeq;': '≽',
+	'succeq;': '⪰',
+	'succnapprox;': '⪺',
+	'succneqq;': '⪶',
+	'succnsim;': '⋩',
+	'succsim;': '≿',
+	'sum;': '∑',
+	'sung;': '♪',
+	sup1: '¹',
+	'sup1;': '¹',
+	sup2: '²',
+	'sup2;': '²',
+	sup3: '³',
+	'sup3;': '³',
+	'sup;': '⊃',
+	'supE;': '⫆',
+	'supdot;': '⪾',
+	'supdsub;': '⫘',
+	'supe;': '⊇',
+	'supedot;': '⫄',
+	'suphsol;': '⟉',
+	'suphsub;': '⫗',
+	'suplarr;': '⥻',
+	'supmult;': '⫂',
+	'supnE;': '⫌',
+	'supne;': '⊋',
+	'supplus;': '⫀',
+	'supset;': '⊃',
+	'supseteq;': '⊇',
+	'supseteqq;': '⫆',
+	'supsetneq;': '⊋',
+	'supsetneqq;': '⫌',
+	'supsim;': '⫈',
+	'supsub;': '⫔',
+	'supsup;': '⫖',
+	'swArr;': '⇙',
+	'swarhk;': '⤦',
+	'swarr;': '↙',
+	'swarrow;': '↙',
+	'swnwar;': '⤪',
+	szlig: 'ß',
+	'szlig;': 'ß',
+	'target;': '⌖',
+	'tau;': 'τ',
+	'tbrk;': '⎴',
+	'tcaron;': 'ť',
+	'tcedil;': 'ţ',
+	'tcy;': 'т',
+	'tdot;': '⃛',
+	'telrec;': '⌕',
+	'tfr;': '𝔱',
+	'there4;': '∴',
+	'therefore;': '∴',
+	'theta;': 'θ',
+	'thetasym;': 'ϑ',
+	'thetav;': 'ϑ',
+	'thickapprox;': '≈',
+	'thicksim;': '∼',
+	'thinsp;': ' ',
+	'thkap;': '≈',
+	'thksim;': '∼',
+	thorn: 'þ',
+	'thorn;': 'þ',
+	'tilde;': '˜',
+	times: '×',
+	'times;': '×',
+	'timesb;': '⊠',
+	'timesbar;': '⨱',
+	'timesd;': '⨰',
+	'tint;': '∭',
+	'toea;': '⤨',
+	'top;': '⊤',
+	'topbot;': '⌶',
+	'topcir;': '⫱',
+	'topf;': '𝕥',
+	'topfork;': '⫚',
+	'tosa;': '⤩',
+	'tprime;': '‴',
+	'trade;': '™',
+	'triangle;': '▵',
+	'triangledown;': '▿',
+	'triangleleft;': '◃',
+	'trianglelefteq;': '⊴',
+	'triangleq;': '≜',
+	'triangleright;': '▹',
+	'trianglerighteq;': '⊵',
+	'tridot;': '◬',
+	'trie;': '≜',
+	'triminus;': '⨺',
+	'triplus;': '⨹',
+	'trisb;': '⧍',
+	'tritime;': '⨻',
+	'trpezium;': '⏢',
+	'tscr;': '𝓉',
+	'tscy;': 'ц',
+	'tshcy;': 'ћ',
+	'tstrok;': 'ŧ',
+	'twixt;': '≬',
+	'twoheadleftarrow;': '↞',
+	'twoheadrightarrow;': '↠',
+	'uArr;': '⇑',
+	'uHar;': '⥣',
+	uacute: 'ú',
+	'uacute;': 'ú',
+	'uarr;': '↑',
+	'ubrcy;': 'ў',
+	'ubreve;': 'ŭ',
+	ucirc: 'û',
+	'ucirc;': 'û',
+	'ucy;': 'у',
+	'udarr;': '⇅',
+	'udblac;': 'ű',
+	'udhar;': '⥮',
+	'ufisht;': '⥾',
+	'ufr;': '𝔲',
+	ugrave: 'ù',
+	'ugrave;': 'ù',
+	'uharl;': '↿',
+	'uharr;': '↾',
+	'uhblk;': '▀',
+	'ulcorn;': '⌜',
+	'ulcorner;': '⌜',
+	'ulcrop;': '⌏',
+	'ultri;': '◸',
+	'umacr;': 'ū',
+	uml: '¨',
+	'uml;': '¨',
+	'uogon;': 'ų',
+	'uopf;': '𝕦',
+	'uparrow;': '↑',
+	'updownarrow;': '↕',
+	'upharpoonleft;': '↿',
+	'upharpoonright;': '↾',
+	'uplus;': '⊎',
+	'upsi;': 'υ',
+	'upsih;': 'ϒ',
+	'upsilon;': 'υ',
+	'upuparrows;': '⇈',
+	'urcorn;': '⌝',
+	'urcorner;': '⌝',
+	'urcrop;': '⌎',
+	'uring;': 'ů',
+	'urtri;': '◹',
+	'uscr;': '𝓊',
+	'utdot;': '⋰',
+	'utilde;': 'ũ',
+	'utri;': '▵',
+	'utrif;': '▴',
+	'uuarr;': '⇈',
+	uuml: 'ü',
+	'uuml;': 'ü',
+	'uwangle;': '⦧',
+	'vArr;': '⇕',
+	'vBar;': '⫨',
+	'vBarv;': '⫩',
+	'vDash;': '⊨',
+	'vangrt;': '⦜',
+	'varepsilon;': 'ϵ',
+	'varkappa;': 'ϰ',
+	'varnothing;': '∅',
+	'varphi;': 'ϕ',
+	'varpi;': 'ϖ',
+	'varpropto;': '∝',
+	'varr;': '↕',
+	'varrho;': 'ϱ',
+	'varsigma;': 'ς',
+	'varsubsetneq;': '⊊︀',
+	'varsubsetneqq;': '⫋︀',
+	'varsupsetneq;': '⊋︀',
+	'varsupsetneqq;': '⫌︀',
+	'vartheta;': 'ϑ',
+	'vartriangleleft;': '⊲',
+	'vartriangleright;': '⊳',
+	'vcy;': 'в',
+	'vdash;': '⊢',
+	'vee;': '∨',
+	'veebar;': '⊻',
+	'veeeq;': '≚',
+	'vellip;': '⋮',
+	'verbar;': '|',
+	'vert;': '|',
+	'vfr;': '𝔳',
+	'vltri;': '⊲',
+	'vnsub;': '⊂⃒',
+	'vnsup;': '⊃⃒',
+	'vopf;': '𝕧',
+	'vprop;': '∝',
+	'vrtri;': '⊳',
+	'vscr;': '𝓋',
+	'vsubnE;': '⫋︀',
+	'vsubne;': '⊊︀',
+	'vsupnE;': '⫌︀',
+	'vsupne;': '⊋︀',
+	'vzigzag;': '⦚',
+	'wcirc;': 'ŵ',
+	'wedbar;': '⩟',
+	'wedge;': '∧',
+	'wedgeq;': '≙',
+	'weierp;': '℘',
+	'wfr;': '𝔴',
+	'wopf;': '𝕨',
+	'wp;': '℘',
+	'wr;': '≀',
+	'wreath;': '≀',
+	'wscr;': '𝓌',
+	'xcap;': '⋂',
+	'xcirc;': '◯',
+	'xcup;': '⋃',
+	'xdtri;': '▽',
+	'xfr;': '𝔵',
+	'xhArr;': '⟺',
+	'xharr;': '⟷',
+	'xi;': 'ξ',
+	'xlArr;': '⟸',
+	'xlarr;': '⟵',
+	'xmap;': '⟼',
+	'xnis;': '⋻',
+	'xodot;': '⨀',
+	'xopf;': '𝕩',
+	'xoplus;': '⨁',
+	'xotime;': '⨂',
+	'xrArr;': '⟹',
+	'xrarr;': '⟶',
+	'xscr;': '𝓍',
+	'xsqcup;': '⨆',
+	'xuplus;': '⨄',
+	'xutri;': '△',
+	'xvee;': '⋁',
+	'xwedge;': '⋀',
+	yacute: 'ý',
+	'yacute;': 'ý',
+	'yacy;': 'я',
+	'ycirc;': 'ŷ',
+	'ycy;': 'ы',
+	yen: '¥',
+	'yen;': '¥',
+	'yfr;': '𝔶',
+	'yicy;': 'ї',
+	'yopf;': '𝕪',
+	'yscr;': '𝓎',
+	'yucy;': 'ю',
+	yuml: 'ÿ',
+	'yuml;': 'ÿ',
+	'zacute;': 'ź',
+	'zcaron;': 'ž',
+	'zcy;': 'з',
+	'zdot;': 'ż',
+	'zeetrf;': 'ℨ',
+	'zeta;': 'ζ',
+	'zfr;': '𝔷',
+	'zhcy;': 'ж',
+	'zigrarr;': '⇝',
+	'zopf;': '𝕫',
+	'zscr;': '𝓏',
+	'zwj;': '‍',
+	'zwnj;': '‌'
+};
+
+const numeric = /&#(x)?([0-9a-f]+);/i;
+const named = new RegExp(
+	`&(${Object.keys(entities)
+		.sort((a, b) => b.length - a.length)
+		.join('|')})`,
+	'g'
+);
+
+/** @param {string} str */
+export function decode(str) {
+	return str
+		.replace(numeric, (_match, hex, code) => String.fromCharCode(hex ? parseInt(code, 16) : +code))
+		.replace(named, (_match, entity) => entities[entity]);
+}
diff --git a/packages/kit/src/core/prerender/entities.spec.js b/packages/kit/src/core/prerender/entities.spec.js
new file mode 100644
index 000000000..92b63827c
--- /dev/null
+++ b/packages/kit/src/core/prerender/entities.spec.js
@@ -0,0 +1,60 @@
+import { test } from 'uvu';
+import * as assert from 'uvu/assert';
+import { decode } from './entities.js';
+
+/*
+Test cases shamelessly stolen from https://github.com/fb55/entities/blob/master/src/decode.spec.ts
+Reproduced under BSD 2 license
+---
+Copyright (c) Felix Böhm
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+const tests = [
+	{ input: '&amp;amp;', output: '&amp;' },
+	{ input: '&amp;#38;', output: '&#38;' },
+	{ input: '&amp;#x26;', output: '&#x26;' },
+	{ input: '&amp;#X26;', output: '&#X26;' },
+	{ input: '&#38;#38;', output: '&#38;' },
+	{ input: '&#x26;#38;', output: '&#38;' },
+	{ input: '&#X26;#38;', output: '&#38;' },
+	{ input: '&#x3a;', output: ':' },
+	{ input: '&#x3A;', output: ':' },
+	{ input: '&#X3a;', output: ':' },
+	{ input: '&#X3A;', output: ':' },
+	{ input: '&>', output: '&>' },
+	{ input: 'id=770&#anchor', output: 'id=770&#anchor' }
+];
+
+for (const { input, output } of tests) {
+	test(input, () => {
+		assert.is(decode(input), output);
+	});
+}
+
+test('should HTML decode partial legacy entity', () => {
+	assert.is(decode('&timesbar'), '×bar');
+});
+
+test('should HTML decode legacy entities according to spec', () => {
+	assert.is(decode('?&image_uri=1&ℑ=2&image=3'), '?&image_uri=1&ℑ=2&image=3');
+});
+
+test('should back out of legacy entities', () => {
+	assert.is(decode('&ampa'), '&a');
+});
+
+test('should parse &nbsp followed by < (#852)', () => {
+	assert.is(decode('&nbsp<'), '\u00a0<');
+});
+
+test.run();
diff --git a/packages/kit/src/core/prerender/fixtures/href-with-character-reference/input.html b/packages/kit/src/core/prerender/fixtures/href-with-character-reference/input.html
new file mode 100644
index 000000000..d0f38a450
--- /dev/null
+++ b/packages/kit/src/core/prerender/fixtures/href-with-character-reference/input.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<link rel="stylesheet" href="/styles.css" />
+		<link rel="icon" href="/favicon.png" />
+	</head>
+	<body>
+		<a href="/test&amp;ampersand">/test&amp;ampersand</a>
+		<a href="/test&quot;quotation">/test&quot;quotation</a>
+		<a href="/test&#9825;decimal">/test&#9825;decimal</a>
+		<a href="/test&#x2665;hex">/test&#x2665;hex</a>
+	</body>
+</html>
diff --git a/packages/kit/src/core/prerender/fixtures/href-with-character-reference/output.json b/packages/kit/src/core/prerender/fixtures/href-with-character-reference/output.json
new file mode 100644
index 000000000..904636f65
--- /dev/null
+++ b/packages/kit/src/core/prerender/fixtures/href-with-character-reference/output.json
@@ -0,0 +1,1 @@
+["/styles.css", "/favicon.png", "/test&ampersand", "/test\"quotation", "/test♡decimal", "/test♥hex"]
diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js
index 563c391b0..e9cd7205e 100644
--- a/packages/kit/src/core/sync/create_manifest_data/index.js
+++ b/packages/kit/src/core/sync/create_manifest_data/index.js
@@ -164,13 +164,16 @@ function create_routes_and_nodes(cwd, config, fallback) {
 
 			const dir = path.join(cwd, routes_base, id);
 
-			const files = fs.readdirSync(dir, {
-				withFileTypes: true
-			});
+			// We can't use withFileTypes because of a NodeJs bug which returns wrong results
+			// with isDirectory() in case of symlinks: https://github.com/nodejs/node/issues/30646
+			const files = fs.readdirSync(dir).map((name) => ({
+				is_dir: fs.statSync(path.join(dir, name)).isDirectory(),
+				name
+			}));
 
 			// process files first
 			for (const file of files) {
-				if (file.isDirectory()) continue;
+				if (file.is_dir) continue;
 				if (!file.name.startsWith('+')) continue;
 				if (!valid_extensions.find((ext) => file.name.endsWith(ext))) continue;
 
@@ -213,7 +216,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
 
 			// then handle children
 			for (const file of files) {
-				if (file.isDirectory()) {
+				if (file.is_dir) {
 					walk(depth + 1, path.posix.join(id, file.name), file.name, route);
 				}
 			}
diff --git a/packages/kit/src/core/sync/write_types/index.js b/packages/kit/src/core/sync/write_types/index.js
index 5204a8de5..a7c82f905 100644
--- a/packages/kit/src/core/sync/write_types/index.js
+++ b/packages/kit/src/core/sync/write_types/index.js
@@ -68,7 +68,7 @@ export async function write_types(config, manifest_data, file) {
 		return;
 	}
 
-	const id = path.posix.relative(config.kit.files.routes, path.dirname(file));
+	const id = posixify(path.relative(config.kit.files.routes, path.dirname(file)));
 
 	const route = manifest_data.routes.find((route) => route.id === id);
 	if (!route) return; // this shouldn't ever happen
diff --git a/packages/kit/src/exports/vite/build/build_server.js b/packages/kit/src/exports/vite/build/build_server.js
index 77011b11e..9b116d423 100644
--- a/packages/kit/src/exports/vite/build/build_server.js
+++ b/packages/kit/src/exports/vite/build/build_server.js
@@ -4,7 +4,13 @@ import { mkdirp, posixify, resolve_entry } from '../../../utils/filesystem.js';
 import { get_vite_config, merge_vite_configs } from '../utils.js';
 import { load_error_page, load_template } from '../../../core/config/index.js';
 import { runtime_directory } from '../../../core/utils.js';
-import { create_build, find_deps, get_default_build_config, is_http_method } from './utils.js';
+import {
+	create_build,
+	find_deps,
+	get_default_build_config,
+	is_http_method,
+	resolve_symlinks
+} from './utils.js';
 import { s } from '../../../utils/misc.js';
 
 /**
@@ -68,7 +74,7 @@ export class Server {
 					get request() {
 						throw new Error('request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details');
 					}
-				}) ?? { message: 'Internal Error' };
+				}) ?? { message: event.routeId ? 'Internal Error' : 'Not Found' };
 			},
 			hooks: null,
 			manifest,
@@ -285,7 +291,7 @@ export async function build_server(options, client) {
 
 			exports.push(
 				`export const component = async () => (await import('../${
-					vite_manifest[node.component].file
+					resolve_symlinks(vite_manifest, node.component).chunk.file
 				}')).default;`,
 				`export const file = '${entry.file}';` // TODO what is this?
 			);
diff --git a/packages/kit/src/exports/vite/build/utils.js b/packages/kit/src/exports/vite/build/utils.js
index 4a0e19e4a..da3134c31 100644
--- a/packages/kit/src/exports/vite/build/utils.js
+++ b/packages/kit/src/exports/vite/build/utils.js
@@ -1,3 +1,5 @@
+import fs from 'fs';
+import path from 'path';
 import * as vite from 'vite';
 import { get_aliases } from '../utils.js';
 
@@ -44,14 +46,14 @@ export function find_deps(manifest, entry, add_dynamic_css) {
 	const stylesheets = new Set();
 
 	/**
-	 * @param {string} file
+	 * @param {string} current
 	 * @param {boolean} add_js
 	 */
-	function traverse(file, add_js) {
-		if (seen.has(file)) return;
-		seen.add(file);
+	function traverse(current, add_js) {
+		if (seen.has(current)) return;
+		seen.add(current);
 
-		const chunk = manifest[file];
+		const { chunk } = resolve_symlinks(manifest, current);
 
 		if (add_js) imports.add(chunk.file);
 
@@ -68,15 +70,31 @@ export function find_deps(manifest, entry, add_dynamic_css) {
 		}
 	}
 
-	traverse(entry, true);
+	const { chunk, file } = resolve_symlinks(manifest, entry);
+
+	traverse(file, true);
 
 	return {
-		file: manifest[entry].file,
+		file: chunk.file,
 		imports: Array.from(imports),
 		stylesheets: Array.from(stylesheets)
 	};
 }
 
+/**
+ * @param {import('vite').Manifest} manifest
+ * @param {string} file
+ */
+export function resolve_symlinks(manifest, file) {
+	while (!manifest[file]) {
+		file = path.relative('.', fs.realpathSync(file));
+	}
+
+	const chunk = manifest[file];
+
+	return { chunk, file };
+}
+
 /**
  * The Vite configuration that we use by default.
  * @param {{
diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js
index 3c28acb48..f9aeb8f3b 100644
--- a/packages/kit/src/exports/vite/dev/index.js
+++ b/packages/kit/src/exports/vite/dev/index.js
@@ -350,7 +350,7 @@ export async function dev(vite, vite_config, svelte_config) {
 						user_hooks.handleError ||
 						(({ error: e }) => {
 							const error = /** @type {Error & { frame?: string }} */ (e);
-							console.error(colors.bold().red(error.message));
+							console.error(colors.bold().red(error.message ?? error)); // Could be anything
 							if (error.frame) {
 								console.error(colors.gray(error.frame));
 							}
@@ -432,7 +432,7 @@ export async function dev(vite, vite_config, svelte_config) {
 											'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
 										);
 									}
-								}) ?? { message: 'Internal Error' }
+								}) ?? { message: event.routeId ? 'Internal Error' : 'Not Found' }
 							);
 						},
 						hooks,
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 106b0c918..d62020e1e 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -1513,7 +1513,10 @@ async function load_data(url, invalid) {
  * @returns {App.PageError}
  */
 function handle_error(error, event) {
-	return hooks.handleError({ error, event }) ?? /** @type {any} */ ({ message: 'Internal Error' });
+	return (
+		hooks.handleError({ error, event }) ??
+		/** @type {any} */ ({ message: event.routeId ? 'Internal Error' : 'Not Found' })
+	);
 }
 
 // TODO remove for 1.0
@@ -1543,7 +1546,8 @@ function add_url_properties(type, target) {
 				throw new Error(
 					`The navigation shape changed - ${type}.${prop} should now be ${type}.url.${prop}`
 				);
-			}
+			},
+			enumerable: false
 		});
 	}
 
diff --git a/packages/kit/src/runtime/server/page/respond_with_error.js b/packages/kit/src/runtime/server/page/respond_with_error.js
index b0e790f02..76fb26f60 100644
--- a/packages/kit/src/runtime/server/page/respond_with_error.js
+++ b/packages/kit/src/runtime/server/page/respond_with_error.js
@@ -4,9 +4,11 @@ import {
 	handle_error_and_jsonify,
 	GENERIC_ERROR,
 	get_option,
-	static_error_page
+	static_error_page,
+	redirect_response
 } from '../utils.js';
 import { create_fetch } from './fetch.js';
+import { HttpError, Redirect } from '../../control.js';
 
 /**
  * @typedef {import('./types.js').Loaded} Loaded
@@ -87,6 +89,16 @@ export async function respond_with_error({ event, options, state, status, error,
 			resolve_opts
 		});
 	} catch (error) {
-		return static_error_page(options, 500, handle_error_and_jsonify(event, options, error).message);
+		// Edge case: If route is a 404 and the user redirects to somewhere from the root layout,
+		// we end up here.
+		if (error instanceof Redirect) {
+			return redirect_response(error.status, error.location, cookies);
+		}
+
+		return static_error_page(
+			options,
+			error instanceof HttpError ? error.status : 500,
+			handle_error_and_jsonify(event, options, error).message
+		);
 	}
 }
diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js
index 333491328..c137f9f4f 100644
--- a/packages/kit/src/runtime/server/utils.js
+++ b/packages/kit/src/runtime/server/utils.js
@@ -70,21 +70,18 @@ export function allowed_methods(mod) {
 
 /** @param {any} data */
 export function data_response(data) {
+	const headers = {
+		'content-type': 'application/javascript',
+		'cache-control': 'private, no-store'
+	};
+
 	try {
-		return new Response(`window.__sveltekit_data = ${devalue(data)}`, {
-			headers: {
-				'content-type': 'application/javascript'
-			}
-		});
+		return new Response(`window.__sveltekit_data = ${devalue(data)}`, { headers });
 	} catch (e) {
 		const error = /** @type {any} */ (e);
 		const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
 		const message = match ? `${error.message} (data.${match[2]})` : error.message;
-		return new Response(`throw new Error(${JSON.stringify(message)})`, {
-			headers: {
-				'content-type': 'application/javascript'
-			}
-		});
+		return new Response(`throw new Error(${JSON.stringify(message)})`, { headers });
 	}
 }
 
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e7bbfd58e..97ccc7dd5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -114,11 +114,12 @@ importers:
 
   packages/adapter-node:
     specifiers:
+      '@rollup/plugin-commonjs': ^22.0.1
       '@rollup/plugin-json': ^4.1.0
+      '@rollup/plugin-node-resolve': ^14.1.0
       '@sveltejs/kit': workspace:*
       '@types/node': ^16.11.36
       c8: ^7.11.3
-      esbuild: ^0.15.7
       node-fetch: ^3.2.4
       polka: ^1.0.0-next.22
       rimraf: ^3.0.2
@@ -127,9 +128,10 @@ importers:
       typescript: ^4.8.2
       uvu: ^0.5.3
     dependencies:
-      esbuild: 0.15.7
-    devDependencies:
+      '@rollup/plugin-commonjs': 22.0.1_rollup@2.78.1
       '@rollup/plugin-json': 4.1.0_rollup@2.78.1
+      '@rollup/plugin-node-resolve': 14.1.0_rollup@2.78.1
+    devDependencies:
       '@sveltejs/kit': link:../kit
       '@types/node': 16.11.42
       c8: 7.11.3
@@ -295,8 +297,8 @@ importers:
       uvu: ^0.5.3
       vite: ^3.1.1
     dependencies:
-      '@types/cookie': 0.5.1
       '@sveltejs/vite-plugin-svelte': 1.0.5_svelte@3.48.0+vite@3.1.1
+      '@types/cookie': 0.5.1
       cookie: 0.5.0
       devalue: 3.1.2
       kleur: 4.1.5
@@ -1085,9 +1087,8 @@ packages:
       glob: 7.2.3
       is-reference: 1.2.1
       magic-string: 0.25.9
-      resolve: 1.22.0
+      resolve: 1.22.1
       rollup: 2.78.1
-    dev: true
 
   /@rollup/plugin-json/4.1.0_rollup@2.78.1:
     resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==}
@@ -1096,7 +1097,6 @@ packages:
     dependencies:
       '@rollup/pluginutils': 3.1.0_rollup@2.78.1
       rollup: 2.78.1
-    dev: true
 
   /@rollup/plugin-node-resolve/14.1.0_rollup@2.78.1:
     resolution: {integrity: sha512-5G2niJroNCz/1zqwXtk0t9+twOSDlG00k1Wfd7bkbbXmwg8H8dvgHdIWAun53Ps/rckfvOC7scDBjuGFg5OaWw==}
@@ -1111,7 +1111,6 @@ packages:
       is-module: 1.0.0
       resolve: 1.22.1
       rollup: 2.78.1
-    dev: true
 
   /@rollup/pluginutils/3.1.0_rollup@2.78.1:
     resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
@@ -1123,7 +1122,6 @@ packages:
       estree-walker: 1.0.1
       picomatch: 2.3.1
       rollup: 2.78.1
-    dev: true
 
   /@rollup/pluginutils/4.2.1:
     resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
@@ -1183,11 +1181,9 @@ packages:
 
   /@types/estree/0.0.39:
     resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
-    dev: true
 
   /@types/estree/0.0.52:
     resolution: {integrity: sha512-BZWrtCU0bMVAIliIV+HJO1f1PR41M7NKjfxrFJwwhKI1KwhwOxYw1SXg9ao+CIMt774nFuGiG6eU+udtbEI9oQ==}
-    dev: true
 
   /@types/gitignore-parser/0.0.0:
     resolution: {integrity: sha512-qxOKILdhl4e639fWdkMySS4tBkRYHkrU2ZNScsMu84EPicliFRr+gAXCLPrs7kTFWdDpgAIlxtUr+YCRtVjsKw==}
@@ -1229,7 +1225,6 @@ packages:
 
   /@types/node/18.0.0:
     resolution: {integrity: sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==}
-    dev: true
 
   /@types/normalize-package-data/2.4.1:
     resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
@@ -1253,7 +1248,6 @@ packages:
     resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
     dependencies:
       '@types/node': 18.0.0
-    dev: true
 
   /@types/sade/1.7.4:
     resolution: {integrity: sha512-6ys13kmtlY0aIOz4KtMdeBD9BHs6vSE3aRcj4vAZqXjypT2el8WZt6799CMjElVgh1cbOH/t3vrpQ4IpwytcPA==}
@@ -1504,7 +1498,6 @@ packages:
   /builtin-modules/3.3.0:
     resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
     engines: {node: '>=6'}
-    dev: true
 
   /c8/7.11.3:
     resolution: {integrity: sha512-6YBmsaNmqRm9OS3ZbIiL2EZgi1+Xc4O24jL3vMYGE6idixYuGdy76rIfIdltSKDj9DpLNrcXSonUTR1miBD0wA==}
@@ -1693,7 +1686,6 @@ packages:
 
   /commondir/1.0.1:
     resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
-    dev: true
 
   /concat-map/0.0.1:
     resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
@@ -2168,7 +2160,6 @@ packages:
 
   /estree-walker/1.0.1:
     resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
-    dev: true
 
   /estree-walker/2.0.2:
     resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
@@ -2622,7 +2613,6 @@ packages:
     engines: {node: '>=6'}
     dependencies:
       builtin-modules: 3.3.0
-    dev: true
 
   /is-callable/1.2.4:
     resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==}
@@ -2641,12 +2631,6 @@ packages:
     dependencies:
       has: 1.0.3
 
-  /is-core-module/2.9.0:
-    resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==}
-    dependencies:
-      has: 1.0.3
-    dev: true
-
   /is-date-object/1.0.5:
     resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
     engines: {node: '>= 0.4'}
@@ -2682,7 +2666,6 @@ packages:
 
   /is-module/1.0.0:
     resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
-    dev: true
 
   /is-negative-zero/2.0.2:
     resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
@@ -2713,7 +2696,6 @@ packages:
     resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
     dependencies:
       '@types/estree': 0.0.52
-    dev: true
 
   /is-regex/1.1.4:
     resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
@@ -2922,7 +2904,6 @@ packages:
     resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
     dependencies:
       sourcemap-codec: 1.4.8
-    dev: true
 
   /magic-string/0.26.2:
     resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==}
@@ -3658,15 +3639,6 @@ packages:
     deprecated: https://github.com/lydell/resolve-url#deprecated
     dev: true
 
-  /resolve/1.22.0:
-    resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
-    hasBin: true
-    dependencies:
-      is-core-module: 2.9.0
-      path-parse: 1.0.7
-      supports-preserve-symlinks-flag: 1.0.0
-    dev: true
-
   /resolve/1.22.1:
     resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
     hasBin: true


Test patch:
diff --git a/packages/kit/test/apps/basics/.gitignore b/packages/kit/test/apps/basics/.gitignore
index fa2f5a579..fad4d3e15 100644
--- a/packages/kit/test/apps/basics/.gitignore
+++ b/packages/kit/test/apps/basics/.gitignore
@@ -1,2 +1,3 @@
 /test/errors.json
 !/.env
+/src/routes/routing/symlink-from
\ No newline at end of file
diff --git a/packages/kit/test/apps/basics/package.json b/packages/kit/test/apps/basics/package.json
index c3da79b25..76e9cf5b7 100644
--- a/packages/kit/test/apps/basics/package.json
+++ b/packages/kit/test/apps/basics/package.json
@@ -7,7 +7,7 @@
 		"build": "vite build",
 		"preview": "vite preview",
 		"check": "svelte-kit sync && tsc && svelte-check",
-		"test": "npm run test:dev && npm run test:build",
+		"test": "node test/setup.js && npm run test:dev && npm run test:build",
 		"test:dev": "rimraf test/errors.json && cross-env DEV=true playwright test",
 		"test:build": "rimraf test/errors.json && playwright test"
 	},
diff --git a/packages/kit/test/apps/basics/src/hooks.client.js b/packages/kit/test/apps/basics/src/hooks.client.js
index 62ee8c1eb..09bfd498b 100644
--- a/packages/kit/test/apps/basics/src/hooks.client.js
+++ b/packages/kit/test/apps/basics/src/hooks.client.js
@@ -1,4 +1,6 @@
 /** @type{import("@sveltejs/kit").HandleClientError} */
-export function handleError({ error }) {
-	return { message: /** @type {Error} */ (error).message };
+export function handleError({ error, event }) {
+	return event.url.pathname.endsWith('404-fallback')
+		? undefined
+		: { message: /** @type {Error} */ (error).message };
 }
diff --git a/packages/kit/test/apps/basics/src/hooks.server.js b/packages/kit/test/apps/basics/src/hooks.server.js
index cb085363b..bf5d6b7fe 100644
--- a/packages/kit/test/apps/basics/src/hooks.server.js
+++ b/packages/kit/test/apps/basics/src/hooks.server.js
@@ -33,7 +33,7 @@ export const handleError = ({ event, error: e }) => {
 		: {};
 	errors[event.url.pathname] = error_to_pojo(error);
 	fs.writeFileSync('test/errors.json', JSON.stringify(errors));
-	return { message: error.message };
+	return event.url.pathname.endsWith('404-fallback') ? undefined : { message: error.message };
 };
 
 export const handle = sequence(
diff --git a/packages/kit/test/apps/basics/src/routes/+layout.server.js b/packages/kit/test/apps/basics/src/routes/+layout.server.js
index cb10abf04..001350db5 100644
--- a/packages/kit/test/apps/basics/src/routes/+layout.server.js
+++ b/packages/kit/test/apps/basics/src/routes/+layout.server.js
@@ -1,6 +1,8 @@
-let should_fail = false;
+import { error, redirect } from '@sveltejs/kit';
+
+let should_fail = '';
 /**
- * @param {boolean} value
+ * @param {string} value
  */
 export function set_should_fail(value) {
 	should_fail = value;
@@ -8,8 +10,16 @@ export function set_should_fail(value) {
 
 export async function load() {
 	if (should_fail) {
-		set_should_fail(false);
-		throw new Error('Failed to load');
+		if (should_fail === 'expected') {
+			set_should_fail('');
+			throw error(401, 'Not allowed');
+		} else if (should_fail === 'unexpected') {
+			set_should_fail('');
+			throw new Error('Failed to load');
+		} else {
+			set_should_fail('');
+			throw redirect(307, '/load');
+		}
 	}
 	// Do NOT make this load function depend on something which would cause it to rerun
 	return {
diff --git a/packages/kit/test/apps/basics/src/routes/errors/error-html/+page.svelte b/packages/kit/test/apps/basics/src/routes/errors/error-html/+page.svelte
index 2fda2e2e0..b8cf7fcdf 100644
--- a/packages/kit/test/apps/basics/src/routes/errors/error-html/+page.svelte
+++ b/packages/kit/test/apps/basics/src/routes/errors/error-html/+page.svelte
@@ -1,8 +1,15 @@
 <script>
-	async function fail() {
-		await fetch('./error-html/make-root-fail');
-		location.reload();
+	/** @param {string} type */
+	async function fail(type) {
+		await fetch(`/errors/error-html/make-root-fail?type=${type}`);
+		if (type === 'redirect') {
+			location.assign(location.href + '/404');
+		} else {
+			location.reload();
+		}
 	}
 </script>
 
-<button on:click={fail}>Let's fail catastrophically</button>
+<button on:click={() => fail('unexpected')}>Unexpected</button>
+<button on:click={() => fail('expected')}>Expected</button>
+<button on:click={() => fail('redirect')}>Redirect</button>
diff --git a/packages/kit/test/apps/basics/src/routes/errors/error-html/make-root-fail/+server.js b/packages/kit/test/apps/basics/src/routes/errors/error-html/make-root-fail/+server.js
index 7c3f14cfd..c3030a9c9 100644
--- a/packages/kit/test/apps/basics/src/routes/errors/error-html/make-root-fail/+server.js
+++ b/packages/kit/test/apps/basics/src/routes/errors/error-html/make-root-fail/+server.js
@@ -1,6 +1,9 @@
 import { set_should_fail } from '../../../+layout.server';
 
-export function GET() {
-	set_should_fail(true);
+/**
+ * @type {import('./$types').RequestHandler} param0
+ */
+export function GET({ url }) {
+	set_should_fail(url.searchParams.get('type'));
 	return new Response();
 }
diff --git a/packages/kit/test/apps/basics/src/routes/load/server-data-nostore/+page.server.js b/packages/kit/test/apps/basics/src/routes/load/server-data-nostore/+page.server.js
new file mode 100644
index 000000000..01e1a121a
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/server-data-nostore/+page.server.js
@@ -0,0 +1,6 @@
+/** @type {import('./$types').PageServerLoad} */
+export function load({ url }) {
+	return {
+		x: url.searchParams.get('x')
+	};
+}
diff --git a/packages/kit/test/apps/basics/src/routes/load/server-data-nostore/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/server-data-nostore/+page.svelte
new file mode 100644
index 000000000..9dc41ad6f
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/server-data-nostore/+page.svelte
@@ -0,0 +1,7 @@
+<script>
+	/** @type {import('./$types').PageData} */
+	export let data;
+</script>
+
+<h1>x: {data.x}</h1>
+<a href="/load/server-data-nostore?x=2">2</a>
diff --git a/packages/kit/test/apps/basics/src/routes/routing/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/+page.svelte
index 61f62ec6f..1d300bf63 100644
--- a/packages/kit/test/apps/basics/src/routes/routing/+page.svelte
+++ b/packages/kit/test/apps/basics/src/routes/routing/+page.svelte
@@ -6,6 +6,7 @@
 
 <a href="/routing/a">a</a>
 <a href="/routing/ambiguous/ok.json" rel="external">ok</a>
+<a href="/routing/symlink-from">symlinked</a>
 <a href="http://localhost:{$page.url.searchParams.get('port')}">elsewhere</a>
 <a href="/static.json">static.json</a>
 
diff --git a/packages/kit/test/apps/basics/src/routes/routing/symlink-to/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/symlink-to/+page.svelte
new file mode 100644
index 000000000..d80b82ddf
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/routing/symlink-to/+page.svelte
@@ -0,0 +1,1 @@
+<h1>symlinked</h1>
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index 42dedf5b7..628e64dd1 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -263,7 +263,7 @@ test.describe('Endpoints', () => {
 	});
 });
 
-test.describe('Errors', () => {
+test.describe.serial('Errors', () => {
 	test('client-side load errors', async ({ page }) => {
 		await page.goto('/errors/load-client');
 
@@ -302,14 +302,29 @@ test.describe('Errors', () => {
 		expect(await page.innerHTML('h1')).toBe('401');
 	});
 
-	test('Root error falls back to error.html', async ({ page }) => {
+	test('Root error falls back to error.html (unexpected error)', async ({ page }) => {
 		await page.goto('/errors/error-html');
-		await page.click('button');
+		await page.click('button:text-is("Unexpected")');
 		expect(await page.textContent('h1')).toBe('Error - 500');
 		expect(await page.textContent('p')).toBe(
 			'This is the static error page with the following message: Failed to load'
 		);
 	});
+
+	test('Root error falls back to error.html (expected error)', async ({ page }) => {
+		await page.goto('/errors/error-html');
+		await page.click('button:text-is("Expected")');
+		expect(await page.textContent('h1')).toBe('Error - 401');
+		expect(await page.textContent('p')).toBe(
+			'This is the static error page with the following message: Not allowed'
+		);
+	});
+
+	test('Root 404 redirects somewhere due to root layout', async ({ page, baseURL }) => {
+		await page.goto('/errors/error-html');
+		await Promise.all([page.waitForNavigation(), page.click('button:text-is("Redirect")')]);
+		expect(page.url()).toBe(baseURL + '/load');
+	});
 });
 
 test.describe('Load', () => {
@@ -416,6 +431,17 @@ test.describe('Load', () => {
 		await expect(page.locator('p')).toHaveText('Count is 2');
 	});
 
+	test('__data.js has cache-control: private, no-store', async ({ page, clicknav }) => {
+		await page.goto('/load/server-data-nostore?x=1');
+
+		const [response] = await Promise.all([
+			page.waitForResponse((response) => /__data\.js/.test(response.url())),
+			clicknav('[href="/load/server-data-nostore?x=2"]')
+		]);
+
+		expect(response.headers()['cache-control']).toBe('private, no-store');
+	});
+
 	if (process.env.DEV) {
 		test('using window.fetch causes a warning', async ({ page }) => {
 			const port = 5173;
diff --git a/packages/kit/test/apps/basics/test/setup.js b/packages/kit/test/apps/basics/test/setup.js
new file mode 100644
index 000000000..9be580053
--- /dev/null
+++ b/packages/kit/test/apps/basics/test/setup.js
@@ -0,0 +1,7 @@
+import fs from 'fs';
+
+if (process.platform !== 'win32') {
+	process.chdir('src/routes/routing');
+	fs.rmSync('symlink-from', { recursive: true, force: true });
+	fs.symlinkSync('symlink-to', 'symlink-from', 'dir');
+}
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index 15c03f300..e63236b8b 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -1633,6 +1633,21 @@ test.describe('Routing', () => {
 		await page.goto('/static');
 		expect(await page.textContent('h1')).toBe('hello');
 	});
+
+	test('shows "Not Found" in 404 case', async ({ page }) => {
+		await page.goto('/404-fallback');
+		expect(await page.textContent('h1')).toBe('404');
+		expect(await page.textContent('p')).toBe('This is your custom error page saying: "Not Found"');
+	});
+
+	if (process.platform !== 'win32') {
+		test('Respects symlinks', async ({ page, clicknav }) => {
+			await page.goto('/routing');
+			await clicknav('[href="/routing/symlink-from"]');
+
+			expect(await page.textContent('h1')).toBe('symlinked');
+		});
+	}
 });
 
 test.describe('Matchers', () => {