fuzzyworld

dao-game / commit

Merge branch 'esbuild' into 'main'

a2ee38e6ca03f958045b463dfc060fbec9a0e0eb · Pavel S · 2022-03-05 00:05:16 +0000 · parents 112f91aeb91c fc2227239d19

Merge branch 'esbuild' into 'main'

Some esbuild-related changes:

See merge request fuzzy-rp/dao-game!3
 .gitignore                          |    8 +
 .vscode/launch.json                 |    5 +-
 .vscode/settings.json               |    8 +
 .vscode/tasks.json                  |   28 +
 package-lock.json                   | 1008 +++++++++++++++++++++++++++++++++++
 package.json                        |   30 ++
 index.html => public/index.html     |   50 +-
 styles.css => public/styles.css     |    0
 yin-yang.svg => public/yin-yang.svg |    0
 readme.md                           |    8 +-
 game.js => src/game.ts              |  157 ++----
 tools/esbuild-build-and-serve.ts    |   44 ++
 tools/esbuild-watch.ts              |    8 +
 tools/esbuild.ts                    |    7 +
 14 files changed, 1231 insertions(+), 130 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9b60cca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+# node modules
+/node_modules/*
+
+# git
+/.git/*
+
+# artifacts
+/public/bundle.js
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index faf7542..b9fd332 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,8 +8,9 @@
             "type": "pwa-chrome",
             "request": "launch",
             "name": "Launch Chrome against localhost",
-            "url": "http://127.0.0.1:5500/",
-            "webRoot": "${workspaceFolder}"
+            "url": "http://localhost:3000/",
+            "webRoot": "${workspaceFolder}/src",
+            "preLaunchTask": "task: npm: start"
         }
     ]
 }
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..1e2d4fa
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,8 @@
+{
+    "cSpell.words": [
+        "esbuild",
+        "outfile",
+        "servedir",
+        "Todos"
+    ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..cfcf68b
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,28 @@
+{
+	"version": "2.0.0",
+	"tasks": [
+		{
+			"label": "task: npm: start",
+			"type": "shell",
+			"isBackground": true,
+			"command": "npm run start",
+			"problemMatcher": [
+				{
+					"pattern": [
+						{
+							"regexp": ".",
+							"file": 1,
+							"location": 2,
+							"message": 3
+						}
+					],
+					"background": {
+						"activeOnStart": true,
+						"beginsPattern": ".",
+						"endsPattern": ".",
+					}
+				}
+			]
+		},
+	]
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..56d4fff
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1008 @@
+{
+  "name": "dao-game",
+  "version": "1.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "dao-game",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "esbuild": "^0.14.25"
+      },
+      "devDependencies": {
+        "rimraf": "^3.0.2",
+        "ts-node": "^10.6.0"
+      }
+    },
+    "node_modules/@cspotcode/source-map-consumer": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
+      "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@cspotcode/source-map-support": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz",
+      "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==",
+      "dev": true,
+      "dependencies": {
+        "@cspotcode/source-map-consumer": "0.8.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@tsconfig/node10": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
+      "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node12": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
+      "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node14": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
+      "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node16": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
+      "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
+      "dev": true
+    },
+    "node_modules/@types/node": {
+      "version": "17.0.21",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
+      "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/acorn": {
+      "version": "8.7.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+      "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-walk": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+      "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "node_modules/create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true
+    },
+    "node_modules/diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.25.tgz",
+      "integrity": "sha512-4JHEIOMNFvK09ziiL+iVmldIhLbn49V4NAVo888tcGFKedEZY/Y8YapfStJ6zSE23tzYPKxqKwQBnQoIO0BI/Q==",
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "esbuild-android-64": "0.14.25",
+        "esbuild-android-arm64": "0.14.25",
+        "esbuild-darwin-64": "0.14.25",
+        "esbuild-darwin-arm64": "0.14.25",
+        "esbuild-freebsd-64": "0.14.25",
+        "esbuild-freebsd-arm64": "0.14.25",
+        "esbuild-linux-32": "0.14.25",
+        "esbuild-linux-64": "0.14.25",
+        "esbuild-linux-arm": "0.14.25",
+        "esbuild-linux-arm64": "0.14.25",
+        "esbuild-linux-mips64le": "0.14.25",
+        "esbuild-linux-ppc64le": "0.14.25",
+        "esbuild-linux-riscv64": "0.14.25",
+        "esbuild-linux-s390x": "0.14.25",
+        "esbuild-netbsd-64": "0.14.25",
+        "esbuild-openbsd-64": "0.14.25",
+        "esbuild-sunos-64": "0.14.25",
+        "esbuild-windows-32": "0.14.25",
+        "esbuild-windows-64": "0.14.25",
+        "esbuild-windows-arm64": "0.14.25"
+      }
+    },
+    "node_modules/esbuild-android-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz",
+      "integrity": "sha512-L5vCUk7TzFbBnoESNoXjU3x9+/+7TDIE/1mTfy/erAfvZAqC+S3sp/Qa9wkypFMcFvN9FzvESkTlpeQDolREtQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-android-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz",
+      "integrity": "sha512-4jv5xPjM/qNm27T5j3ZEck0PvjgQtoMHnz4FzwF5zNP56PvY2CT0WStcAIl6jNlsuDdN63rk2HRBIsO6xFbcFw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz",
+      "integrity": "sha512-TGp8tuudIxOyWd1+8aYPxQmC1ZQyvij/AfNBa35RubixD0zJ1vkKHVAzo0Zao1zcG6pNqiSyzfPto8vmg0s7oA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz",
+      "integrity": "sha512-oTcDgdm0MDVEmw2DWu8BV68pYuImpFgvWREPErBZmNA4MYKGuBRaCiJqq6jZmBR1x+3y1DWCjez+5uLtuAm6mw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz",
+      "integrity": "sha512-ueAqbnMZ8arnuLH8tHwTCQYeptnHOUV7vA6px6j4zjjQwDx7TdP7kACPf3TLZLdJQ3CAD1XCvQ2sPhX+8tacvQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz",
+      "integrity": "sha512-+ZVWud2HKh+Ob6k/qiJWjBtUg4KmJGGmbvEXXW1SNKS7hW7HU+Zq2ZCcE1akFxOPkVB+EhOty/sSek30tkCYug==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-32": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz",
+      "integrity": "sha512-3OP/lwV3kCzEz45tobH9nj+uE4ubhGsfx+tn0L26WAGtUbmmcRpqy7XRG/qK7h1mClZ+eguIANcQntYMdYklfw==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz",
+      "integrity": "sha512-+aKHdHZmX9qwVlQmu5xYXh7GsBFf4TWrePgeJTalhXHOG7NNuUwoHmketGiZEoNsWyyqwH9rE5BC+iwcLY30Ug==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz",
+      "integrity": "sha512-aTLcE2VBoLydL943REcAcgnDi3bHtmULSXWLbjtBdtykRatJVSxKMjK9YlBXUZC4/YcNQfH7AxwVeQr9fNxPhw==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz",
+      "integrity": "sha512-UxfenPx/wSZx55gScCImPtXekvZQLI2GW3qe5dtlmU7luiqhp5GWPzGeQEbD3yN3xg/pHc671m5bma5Ns7lBHw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-mips64le": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz",
+      "integrity": "sha512-wLWYyqVfYx9Ur6eU5RT92yJVsaBGi5RdkoWqRHOqcJ38Kn60QMlcghsKeWfe9jcYut8LangYZ98xO1LxIoSXrQ==",
+      "cpu": [
+        "mips64el"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-ppc64le": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz",
+      "integrity": "sha512-0dR6Csl6Zas3g4p9ULckEl8Mo8IInJh33VCJ3eaV1hj9+MHGdmDOakYMN8MZP9/5nl+NU/0ygpd14cWgy8uqRw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-riscv64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz",
+      "integrity": "sha512-J4d20HDmTrgvhR0bdkDhvvJGaikH3LzXQnNaseo8rcw9Yqby9A90gKUmWpfwqLVNRILvNnAmKLfBjCKU9ajg8w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-s390x": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz",
+      "integrity": "sha512-YI2d5V6nTE73ZnhEKQD7MtsPs1EtUZJ3obS21oxQxGbbRw1G+PtJKjNyur+3t6nzHP9oTg6GHQ3S3hOLLmbDIQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-netbsd-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz",
+      "integrity": "sha512-TKIVgNWLUOkr+Exrye70XTEE1lJjdQXdM4tAXRzfHE9iBA7LXWcNtVIuSnphTqpanPzTDFarF0yqq4kpbC6miA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-openbsd-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz",
+      "integrity": "sha512-QgFJ37A15D7NIXBTYEqz29+uw3nNBOIyog+3kFidANn6kjw0GHZ0lEYQn+cwjyzu94WobR+fes7cTl/ZYlHb1A==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-sunos-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz",
+      "integrity": "sha512-rmWfjUItYIVlqr5EnTH1+GCxXiBOC42WBZ3w++qh7n2cS9Xo0lO5pGSG2N+huOU2fX5L+6YUuJ78/vOYvefeFw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-32": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz",
+      "integrity": "sha512-HGAxVUofl3iUIz9W10Y9XKtD0bNsK9fBXv1D55N/ljNvkrAYcGB8YCm0v7DjlwtyS6ws3dkdQyXadbxkbzaKOA==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz",
+      "integrity": "sha512-TirEohRkfWU9hXLgoDxzhMQD1g8I2mOqvdQF2RS9E/wbkORTAqJHyh7wqGRCQAwNzdNXdg3JAyhQ9/177AadWA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz",
+      "integrity": "sha512-4ype9ERiI45rSh+R8qUoBtaj6kJvUOI7oVLhKqPEpcF4Pa5PpT3hm/mXAyotJHREkHpM87PAJcA442mLnbtlNA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "node_modules/glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "node_modules/make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
+    "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/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/ts-node": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.6.0.tgz",
+      "integrity": "sha512-CJen6+dfOXolxudBQXnVjRVvYTmTWbyz7cn+xq2XTsvnaXbHqr4gXSCNbS2Jj8yTZMuGwUoBESLaOkLascVVvg==",
+      "dev": true,
+      "dependencies": {
+        "@cspotcode/source-map-support": "0.7.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.0",
+        "yn": "3.1.1"
+      },
+      "bin": {
+        "ts-node": "dist/bin.js",
+        "ts-node-cwd": "dist/bin-cwd.js",
+        "ts-node-script": "dist/bin-script.js",
+        "ts-node-transpile-only": "dist/bin-transpile.js",
+        "ts-script": "dist/bin-script-deprecated.js"
+      },
+      "peerDependencies": {
+        "@swc/core": ">=1.2.50",
+        "@swc/wasm": ">=1.2.50",
+        "@types/node": "*",
+        "typescript": ">=2.7"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/wasm": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/typescript": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+      "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==",
+      "dev": true,
+      "peer": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=4.2.0"
+      }
+    },
+    "node_modules/v8-compile-cache-lib": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz",
+      "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==",
+      "dev": true
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "node_modules/yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    }
+  },
+  "dependencies": {
+    "@cspotcode/source-map-consumer": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
+      "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==",
+      "dev": true
+    },
+    "@cspotcode/source-map-support": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz",
+      "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==",
+      "dev": true,
+      "requires": {
+        "@cspotcode/source-map-consumer": "0.8.0"
+      }
+    },
+    "@tsconfig/node10": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
+      "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
+      "dev": true
+    },
+    "@tsconfig/node12": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
+      "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
+      "dev": true
+    },
+    "@tsconfig/node14": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
+      "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
+      "dev": true
+    },
+    "@tsconfig/node16": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
+      "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "17.0.21",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
+      "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
+      "dev": true,
+      "peer": true
+    },
+    "acorn": {
+      "version": "8.7.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+      "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+      "dev": true
+    },
+    "acorn-walk": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+      "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+      "dev": true
+    },
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true
+    },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
+    "esbuild": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.25.tgz",
+      "integrity": "sha512-4JHEIOMNFvK09ziiL+iVmldIhLbn49V4NAVo888tcGFKedEZY/Y8YapfStJ6zSE23tzYPKxqKwQBnQoIO0BI/Q==",
+      "requires": {
+        "esbuild-android-64": "0.14.25",
+        "esbuild-android-arm64": "0.14.25",
+        "esbuild-darwin-64": "0.14.25",
+        "esbuild-darwin-arm64": "0.14.25",
+        "esbuild-freebsd-64": "0.14.25",
+        "esbuild-freebsd-arm64": "0.14.25",
+        "esbuild-linux-32": "0.14.25",
+        "esbuild-linux-64": "0.14.25",
+        "esbuild-linux-arm": "0.14.25",
+        "esbuild-linux-arm64": "0.14.25",
+        "esbuild-linux-mips64le": "0.14.25",
+        "esbuild-linux-ppc64le": "0.14.25",
+        "esbuild-linux-riscv64": "0.14.25",
+        "esbuild-linux-s390x": "0.14.25",
+        "esbuild-netbsd-64": "0.14.25",
+        "esbuild-openbsd-64": "0.14.25",
+        "esbuild-sunos-64": "0.14.25",
+        "esbuild-windows-32": "0.14.25",
+        "esbuild-windows-64": "0.14.25",
+        "esbuild-windows-arm64": "0.14.25"
+      }
+    },
+    "esbuild-android-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz",
+      "integrity": "sha512-L5vCUk7TzFbBnoESNoXjU3x9+/+7TDIE/1mTfy/erAfvZAqC+S3sp/Qa9wkypFMcFvN9FzvESkTlpeQDolREtQ==",
+      "optional": true
+    },
+    "esbuild-android-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz",
+      "integrity": "sha512-4jv5xPjM/qNm27T5j3ZEck0PvjgQtoMHnz4FzwF5zNP56PvY2CT0WStcAIl6jNlsuDdN63rk2HRBIsO6xFbcFw==",
+      "optional": true
+    },
+    "esbuild-darwin-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz",
+      "integrity": "sha512-TGp8tuudIxOyWd1+8aYPxQmC1ZQyvij/AfNBa35RubixD0zJ1vkKHVAzo0Zao1zcG6pNqiSyzfPto8vmg0s7oA==",
+      "optional": true
+    },
+    "esbuild-darwin-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz",
+      "integrity": "sha512-oTcDgdm0MDVEmw2DWu8BV68pYuImpFgvWREPErBZmNA4MYKGuBRaCiJqq6jZmBR1x+3y1DWCjez+5uLtuAm6mw==",
+      "optional": true
+    },
+    "esbuild-freebsd-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz",
+      "integrity": "sha512-ueAqbnMZ8arnuLH8tHwTCQYeptnHOUV7vA6px6j4zjjQwDx7TdP7kACPf3TLZLdJQ3CAD1XCvQ2sPhX+8tacvQ==",
+      "optional": true
+    },
+    "esbuild-freebsd-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz",
+      "integrity": "sha512-+ZVWud2HKh+Ob6k/qiJWjBtUg4KmJGGmbvEXXW1SNKS7hW7HU+Zq2ZCcE1akFxOPkVB+EhOty/sSek30tkCYug==",
+      "optional": true
+    },
+    "esbuild-linux-32": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz",
+      "integrity": "sha512-3OP/lwV3kCzEz45tobH9nj+uE4ubhGsfx+tn0L26WAGtUbmmcRpqy7XRG/qK7h1mClZ+eguIANcQntYMdYklfw==",
+      "optional": true
+    },
+    "esbuild-linux-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz",
+      "integrity": "sha512-+aKHdHZmX9qwVlQmu5xYXh7GsBFf4TWrePgeJTalhXHOG7NNuUwoHmketGiZEoNsWyyqwH9rE5BC+iwcLY30Ug==",
+      "optional": true
+    },
+    "esbuild-linux-arm": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz",
+      "integrity": "sha512-aTLcE2VBoLydL943REcAcgnDi3bHtmULSXWLbjtBdtykRatJVSxKMjK9YlBXUZC4/YcNQfH7AxwVeQr9fNxPhw==",
+      "optional": true
+    },
+    "esbuild-linux-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz",
+      "integrity": "sha512-UxfenPx/wSZx55gScCImPtXekvZQLI2GW3qe5dtlmU7luiqhp5GWPzGeQEbD3yN3xg/pHc671m5bma5Ns7lBHw==",
+      "optional": true
+    },
+    "esbuild-linux-mips64le": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz",
+      "integrity": "sha512-wLWYyqVfYx9Ur6eU5RT92yJVsaBGi5RdkoWqRHOqcJ38Kn60QMlcghsKeWfe9jcYut8LangYZ98xO1LxIoSXrQ==",
+      "optional": true
+    },
+    "esbuild-linux-ppc64le": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz",
+      "integrity": "sha512-0dR6Csl6Zas3g4p9ULckEl8Mo8IInJh33VCJ3eaV1hj9+MHGdmDOakYMN8MZP9/5nl+NU/0ygpd14cWgy8uqRw==",
+      "optional": true
+    },
+    "esbuild-linux-riscv64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz",
+      "integrity": "sha512-J4d20HDmTrgvhR0bdkDhvvJGaikH3LzXQnNaseo8rcw9Yqby9A90gKUmWpfwqLVNRILvNnAmKLfBjCKU9ajg8w==",
+      "optional": true
+    },
+    "esbuild-linux-s390x": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz",
+      "integrity": "sha512-YI2d5V6nTE73ZnhEKQD7MtsPs1EtUZJ3obS21oxQxGbbRw1G+PtJKjNyur+3t6nzHP9oTg6GHQ3S3hOLLmbDIQ==",
+      "optional": true
+    },
+    "esbuild-netbsd-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz",
+      "integrity": "sha512-TKIVgNWLUOkr+Exrye70XTEE1lJjdQXdM4tAXRzfHE9iBA7LXWcNtVIuSnphTqpanPzTDFarF0yqq4kpbC6miA==",
+      "optional": true
+    },
+    "esbuild-openbsd-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz",
+      "integrity": "sha512-QgFJ37A15D7NIXBTYEqz29+uw3nNBOIyog+3kFidANn6kjw0GHZ0lEYQn+cwjyzu94WobR+fes7cTl/ZYlHb1A==",
+      "optional": true
+    },
+    "esbuild-sunos-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz",
+      "integrity": "sha512-rmWfjUItYIVlqr5EnTH1+GCxXiBOC42WBZ3w++qh7n2cS9Xo0lO5pGSG2N+huOU2fX5L+6YUuJ78/vOYvefeFw==",
+      "optional": true
+    },
+    "esbuild-windows-32": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz",
+      "integrity": "sha512-HGAxVUofl3iUIz9W10Y9XKtD0bNsK9fBXv1D55N/ljNvkrAYcGB8YCm0v7DjlwtyS6ws3dkdQyXadbxkbzaKOA==",
+      "optional": true
+    },
+    "esbuild-windows-64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz",
+      "integrity": "sha512-TirEohRkfWU9hXLgoDxzhMQD1g8I2mOqvdQF2RS9E/wbkORTAqJHyh7wqGRCQAwNzdNXdg3JAyhQ9/177AadWA==",
+      "optional": true
+    },
+    "esbuild-windows-arm64": {
+      "version": "0.14.25",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz",
+      "integrity": "sha512-4ype9ERiI45rSh+R8qUoBtaj6kJvUOI7oVLhKqPEpcF4Pa5PpT3hm/mXAyotJHREkHpM87PAJcA442mLnbtlNA==",
+      "optional": true
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "ts-node": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.6.0.tgz",
+      "integrity": "sha512-CJen6+dfOXolxudBQXnVjRVvYTmTWbyz7cn+xq2XTsvnaXbHqr4gXSCNbS2Jj8yTZMuGwUoBESLaOkLascVVvg==",
+      "dev": true,
+      "requires": {
+        "@cspotcode/source-map-support": "0.7.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.0",
+        "yn": "3.1.1"
+      }
+    },
+    "typescript": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+      "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==",
+      "dev": true,
+      "peer": true
+    },
+    "v8-compile-cache-lib": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz",
+      "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==",
+      "dev": true
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a518360
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+  "name": "dao-game",
+  "version": "1.0.0",
+  "description": "Dao (game)",
+  "main": "app.js",
+  "scripts": {
+    "clean": "rimraf public/bundle.js",
+    "build": "ts-node tools/esbuild.js",
+    "build-watch": "ts-node tools/esbuild-watch.ts",
+    "start": "ts-node tools/esbuild-build-and-serve.ts",
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://gitlab.com/fuzzy-rp/dao-game.git"
+  },
+  "author": "",
+  "license": "ISC",
+  "bugs": {
+    "url": "https://gitlab.com/fuzzy-rp/dao-game/issues"
+  },
+  "homepage": "https://gitlab.com/fuzzy-rp/dao-game#readme",
+  "dependencies": {
+    "esbuild": "^0.14.25"
+  },
+  "devDependencies": {
+    "rimraf": "^3.0.2",
+    "ts-node": "^10.6.0"
+  }
+}
\ No newline at end of file
diff --git a/index.html b/public/index.html
similarity index 58%
rename from index.html
rename to public/index.html
index e7285c8..033ea5a 100644
--- a/index.html
+++ b/public/index.html
@@ -4,23 +4,35 @@
     <head>
         <meta charset="utf-8">
         <title>Dao Game</title>
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <link rel="icon" href="/yin-yang.svg" type="image/svg+xml">
-        <link rel="stylesheet" href="styles.css" />
-        <script defer type="module" src="game.js"></script>
+        <meta name="viewport"
+              content="width=device-width, initial-scale=1">
+        <link rel="icon"
+              href="yin-yang.svg"
+              type="image/svg+xml">
+        <link rel="stylesheet"
+              href="styles.css" />
+        <script defer
+                src="bundle.js"></script>
     </head>
 
     <body>
+
         <div class="invisible">
-            <p class='piece red-piece' id='default-red-piece'></p>
-            <p class='piece blue-piece' id='default-blue-piece'></p>
+            <p class='piece red-piece'
+               id='default-red-piece'></p>
+            <p class='piece blue-piece'
+               id='default-blue-piece'></p>
         </div>
         <main>
+
             <div class="controls">
+
                 <span class="red-turn-text">
-                    Red's turn
+                    Red's turn!
                 </span>
+
                 <span class="divider">|</span>
+
                 <span class="blue-turn-text">
                     Blue's turn
                 </span>
@@ -30,23 +42,30 @@
             </table>
 
             <div class="controls">
-                <button type="button" class="button" id="undo-button">
+                <button type="button"
+                        class="button"
+                        id="undo-button">
                     Undo
                 </button>
+
                 <span class="divider">|</span>
-                <button type="button" class="button" id="reset-button">
+                <button type="button"
+                        class="button"
+                        id="reset-button">
                     Reset
                 </button>
             </div>
         </main>
         <hr />
+
         <div class="title">
             <p>
-                Board game <a
-                    href='https://en.wikipedia.org/wiki/Dao_(game)'
-                    target='_blank'>Dao</a>.
+                Board game <a href='https://en.wikipedia.org/wiki/Dao_(game)'
+                   target='_blank'
+                   rel="noopener">Dao</a>.
             </p>
         </div>
+
         <div class="info">
             The game consists of only one simple rule and three ways
             to
@@ -61,13 +80,16 @@
             square anywhere on the board.
         </div>
         <hr />
+
         <div class="info">
             <p>
                 Here is the
-                <a href='https://gitlab.com/fuzzy-rp/dao-game' target="_blank">source
+                <a href='https://gitlab.com/fuzzy-rp/dao-game'
+                   target="_blank"
+                   rel="noopener">source
                     code</a>
             </p>
         </div>
     </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/styles.css b/public/styles.css
similarity index 100%
rename from styles.css
rename to public/styles.css
diff --git a/yin-yang.svg b/public/yin-yang.svg
similarity index 100%
rename from yin-yang.svg
rename to public/yin-yang.svg
diff --git a/readme.md b/readme.md
index ef00c5b..a5af0f1 100644
--- a/readme.md
+++ b/readme.md
@@ -2,14 +2,16 @@
 
 ## Running
 
-1. Start "Live Server"
-2. Launch default debug
-3. Sometimes, presence of breakpoints breaks the debug page entirely
+1. `npm run start`
 
 ## Gitlab Pages
 
 [Location](https://fuzzy-rp.gitlab.io/dao-game/)
 
+## Esbuild
+
+This project is using [esbuild](https://esbuild.github.io/getting-started/#build-scripts) for creating a bundle.
+
 ## Todos
 
 1. Use esbuild and rewrite app in TypeScript
diff --git a/game.js b/src/game.ts
similarity index 78%
rename from game.js
rename to src/game.ts
index 6c292be..119341f 100644
--- a/game.js
+++ b/src/game.ts
@@ -1,55 +1,36 @@
 class Coord {
-    /**
-     * Creates an instance of Coord.
-     * @param {number} row
-     * @param {number} col
-     * @memberof Coord
-     */
-    constructor(row, col) {
-        /** @type {number} */
+    public row: number;
+    public col: number;
+
+    constructor(row: number, col: number) {
         this.row = row;
-        /** @type {number} */
         this.col = col;
     }
 
-    /**
-     * @param {Coord} coord
-     * @returns {Coord}
-     * @memberof Coord
-     */
-    add(coord) {
+    public add(coord: Coord): Coord {
         return new Coord(this.row + coord.row, this.col + coord.col);
     }
 
-    /**
-     * @param {Coord} coord
-     * @returns {Coord}
-     * @memberof Coord
-     */
-    sub(coord) {
+    sub(coord: Coord): Coord {
         return new Coord(this.row - coord.row, this.col - coord.col);
     }
 
-    /**
-     * @param {Coord} otherCoord
-     * @memberof Coord
-     */
-    equals(otherCoord) {
+    equals(otherCoord: Coord): boolean {
         const areEqual = (this.col === otherCoord.col && this.row === otherCoord.row);
         return areEqual;
     }
 
     // clock face directions
-    static get allDirections() {
+    static get allDirections(): Coord[] {
         const allDirections = [
-            { row: 1, col: 0 }, // up
-            { row: 1, col: 1 }, // up-right
-            { row: 0, col: 1 }, // right
-            { row: -1, col: 1 }, // right-down
-            { row: -1, col: 0 }, // down
-            { row: -1, col: -1 }, // down-left
-            { row: 0, col: -1 }, // left
-            { row: 1, col: -1 }, // left-up
+            new Coord(1, 0), // up
+            new Coord(1, 1), // up-right
+            new Coord(0, 1), // right
+            new Coord(-1, 1), // right-down
+            new Coord(-1, 0), // down
+            new Coord(-1, -1), // down-left
+            new Coord(0, -1), // left
+            new Coord(1, -1), // left-up
         ]
         return allDirections;
     }
@@ -78,19 +59,11 @@ const gameBoard = {
     //     null, 2, 3, null,
     //     6, null, null, 7
     // ],
-    /**
-     * @param {Coord} data
-     * @return {number} 
-     */
-    coordToBoardIndex: ({ row, col }) => {
+    coordToBoardIndex: ({ row, col }: Coord) => {
         const index = row * 4 + col;
         return index;
     },
-    /**
-     * @param {number} index
-     * @return {Coord} 
-     */
-    boardIndexToCoord: (index) => {
+    boardIndexToCoord: (index: number) => {
         const col = index % 4;
         const row = Math.floor(index / 4);
         return new Coord(row, col);
@@ -98,24 +71,18 @@ const gameBoard = {
 }
 
 class Move {
-    /**
-     * Creates an instance of Move.
-     * @param {number} sourceIndex
-     * @param {number} destIndex
-     * @memberof Move
-     */
-    constructor(sourceIndex, destIndex) {
-        /** @type {number} */
+    public sourceIndex: number;
+    public destIndex: number;
+    constructor(sourceIndex: number, destIndex: number) {
         this.sourceIndex = sourceIndex;
-        /** @type {number} */
         this.destIndex = destIndex;
     }
 }
-const player = {
+
+
+const player: { isRedTurn: boolean, selectedPieceId: number | undefined, moveHistory: Move[] } = {
     isRedTurn: false,
-    /** @type {number | undefined} */
     selectedPieceId: undefined,
-    /** @type {Move[]} */
     moveHistory: [],
 }
 
@@ -125,25 +92,23 @@ const dom = {
     defaultBluePiece: document.getElementById('default-blue-piece'),
     cells: () => Array.from(document.querySelectorAll("td")),
     allPieces: () => Array.from(document.querySelectorAll('.piece')),
-    playerPieces: (isRedTurn) => isRedTurn ?
+    playerPieces: (isRedTurn: boolean) => isRedTurn ?
         Array.from(document.querySelectorAll('.red-piece:not(#default-red-piece')) :
         Array.from(document.querySelectorAll('.blue-piece:not(#default-blue-piece')),
     redTurnText: () => Array.from(document.querySelectorAll(".red-turn-text")),
     blueTurnText: () => Array.from(document.querySelectorAll(".blue-turn-text")),
-    resetButton: document.getElementById('reset-button'),
-    undoButton: document.getElementById('undo-button'),
+    resetButton: document.getElementById('reset-button') as HTMLInputElement,
+    undoButton: document.getElementById('undo-button') as HTMLInputElement,
 }
 
-const listenerManager = {
-    /** @type {{element: Element, type: string, listener: (event: Event) => void}[]} */
+const listenerManager: {
+    listeners: { element: Element, type: string, listener: (event: Event) => void }[],
+    addListener: (element: Element, type: string, listener: (event: Event) => void) => void,
+    removeListeners: () => void,
+} = {
     listeners: [],
 
-    /**
-     * @param {Element} element
-     * @param {string} eventName
-     * @param {Event => void} callback
-     */
-    addListener: (element, type, listener) => {
+    addListener: (element: Element, type: string, listener: (event: Event) => void): void => {
         const { listeners } = listenerManager;
         element.addEventListener(type, listener);
         listeners.push({ element, type, listener });
@@ -179,14 +144,10 @@ const resetBoard = () => {
     listenerManager.removeListeners();
 };
 
-/**
- * @param {number} sourceIndex
- * @param {number} destIndex
- */
-const movePiece = async (sourceIndex, destIndex) => {
+const movePiece = async (sourceIndex: number, destIndex: number) => {
     const pieceId = gameBoard.board[sourceIndex];
     // find current piece
-    const piece = document.getElementById(pieceId);
+    const piece = document.getElementById(pieceId.toString());
     // unselect piece
     piece.classList.remove(constants.selectedPieceClassName);
 
@@ -219,21 +180,9 @@ const movePiece = async (sourceIndex, destIndex) => {
 
 const nextMove = () => {
     const setActivePlayerPieces = () => {
-        /** @param {Event} event */
-        const selectPiece = (event) => {
-            /**
-             * @param {number} pieceId
-             * @returns {number[]}
-             */
-            const getAllowedMoveIndicesForIndex = (boardIndex) => {
-                /**
-                 *
-                 *
-                 * @param {Coord} startCoord
-                 * @param {Coord} direction
-                 * @returns {Coord}
-                 */
-                const getFarthestCoord = (startCoord, direction) => {
+        const selectPiece = (event: Event) => {
+            const getAllowedMoveIndicesForIndex = (boardIndex: number): number[] => {
+                const getFarthestCoord = (startCoord: Coord, direction: Coord): Coord => {
                     let farthestPos = undefined;
                     let testPos = startCoord;
                     while (true) {
@@ -265,7 +214,7 @@ const nextMove = () => {
 
             const previousSelectedPieceId = player.selectedPieceId;
             if (previousSelectedPieceId !== undefined) {
-                const previousPiece = document.getElementById(previousSelectedPieceId);
+                const previousPiece = document.getElementById(previousSelectedPieceId.toString());
                 previousPiece.classList.remove(constants.selectedPieceClassName);
                 dom.cells().forEach(cell => {
                     cell.classList.remove(constants.blinkingClassName);
@@ -273,8 +222,7 @@ const nextMove = () => {
             }
 
             // read event data
-            /** @type {Element} */
-            const piece = event.target;
+            const piece = event.target as Element;
             const pieceId = parseInt(piece.id);
 
             // set as selected
@@ -344,16 +292,8 @@ const nextMove = () => {
 };
 
 const checkForWin = () => {
-    /**
-     * @param {Element[]} elements
-     * @return {Coord[]} 
-     */
-    const elementsToCoords = (elements) => {
-        /**
-         * @param {Element[]} elements
-         * @returns {number[]}
-         */
-        const elementsToBoardIndexes = (elements) => {
+    const elementsToCoords = (elements: Element[]): Coord[] => {
+        const elementsToBoardIndexes = (elements: Element[]): number[] => {
             const indices = elements.map(pp => {
                 const pieceId = Number.parseInt(pp.id);
                 const pieceIndex = gameBoard.board.indexOf(pieceId);
@@ -366,11 +306,7 @@ const checkForWin = () => {
         const coords = indices.map(pieceIndex => gameBoard.boardIndexToCoord(pieceIndex));
         return coords;
     };
-    /**
-     * @param {Coord[]} coords
-     * @returns {boolean}
-     */
-    const doCoordsFormSquare = (coords) => {
+    const doCoordsFormSquare = (coords: Coord[]): boolean => {
         const allRows = coords.map(c => c.row);
         const allCols = coords.map(c => c.col);
         const min = new Coord(Math.min(...allRows), Math.min(...allCols));
@@ -382,8 +318,7 @@ const checkForWin = () => {
 
     const playerCoords = elementsToCoords(dom.playerPieces(player.isRedTurn))
     const otherCoords = elementsToCoords(dom.playerPieces(!player.isRedTurn));
-    /** @type {Coord[]} */
-    const squareCoords = [
+    const squareCoords: Coord[] = [
         new Coord(0, 0),
         new Coord(3, 0),
         new Coord(3, 3),
@@ -444,8 +379,8 @@ const init = () => {
         if (value !== null) {
             const isRed = value < 4;
             const defaultPiece = isRed ? dom.defaultRedPiece : dom.defaultBluePiece;
-            const newPiece = defaultPiece.cloneNode(true);
-            newPiece.id = value;
+            const newPiece = defaultPiece.cloneNode(true) as HTMLParagraphElement;
+            newPiece.id = value.toString();
             dom.cells()[index].appendChild(newPiece);
         }
     });
diff --git a/tools/esbuild-build-and-serve.ts b/tools/esbuild-build-and-serve.ts
new file mode 100644
index 0000000..3be8fb1
--- /dev/null
+++ b/tools/esbuild-build-and-serve.ts
@@ -0,0 +1,44 @@
+import childProcess from 'child_process';
+import esbuild from 'esbuild';
+import http from 'http';
+
+const hostPort = 3000;
+
+const serveOptions: esbuild.ServeOptions = {
+    servedir: 'public'
+};
+const buildOptions: esbuild.BuildOptions = {
+    entryPoints: ['src/game.ts'],
+    bundle: true,
+    outfile: 'public/bundle.js',
+    sourcemap: true
+};
+esbuild.serve(serveOptions, buildOptions)
+    .then(result => {
+        const { host, port } = result;
+        http
+            .createServer((req, res) => {
+                const options: http.RequestOptions = {
+                    hostname: host,
+                    port: port,
+                    path: req.url,
+                    method: req.method,
+                    headers: req.headers
+                };
+                const proxyReq = http.request(options, proxyRes => {
+                    if (proxyRes.statusCode === 404) {
+                        res.writeHead(404, { 'Content-Type': 'text/html' });
+                        res.end('<h1>A custom 404 page</h1>');
+                        return;
+                    }
+
+                    res.writeHead(proxyRes.statusCode!, proxyRes.headers);
+                    proxyRes.pipe(res, { end: true });
+                });
+
+                req.pipe(proxyReq, { end: true });
+            })
+            .listen(hostPort, () => {
+                childProcess.exec(`start http://localhost:${hostPort}/`);
+            });
+    });
\ No newline at end of file
diff --git a/tools/esbuild-watch.ts b/tools/esbuild-watch.ts
new file mode 100644
index 0000000..602f1c3
--- /dev/null
+++ b/tools/esbuild-watch.ts
@@ -0,0 +1,8 @@
+import esbuild from 'esbuild';
+
+esbuild.build({
+    entryPoints: ['src/game.ts'],
+    bundle: true,
+    outfile: 'public/bundle.js',
+    watch: true,
+}).catch(() => process.exit(1))
\ No newline at end of file
diff --git a/tools/esbuild.ts b/tools/esbuild.ts
new file mode 100644
index 0000000..24ef6db
--- /dev/null
+++ b/tools/esbuild.ts
@@ -0,0 +1,7 @@
+import esbuild from 'esbuild';
+
+esbuild.build({
+    entryPoints: ['src/game.ts'],
+    bundle: true,
+    outfile: 'public/bundle.js',
+}).catch(() => process.exit(1))
\ No newline at end of file