Explorar o código

[JavaScript] Add HyperExpress (#8305)

* [JavaScript] Add HyperExpress

* [HyperExpress] Create readme.md

* Rename readme.md to README.md

* [HyperExpress] Tidy up codes

* [HyperExpress] Add mysql

* [HyperExpress] Tweaking github actions for db pool max connections

* [HyperExpress] Instantiating json for each request (#1)

* [HyperExpress] Instantiating json for each request

* [HyperExpress] add max connections to postgres

* [HyperExpress] Removing postgres pool max connections

* [HyperExpress] Fix starting app in single instance

* [HyperExpress] Remove unused scripts

* [HyperExpress] Removing postgres pool max connections
Imam Fahrur Rofi %!s(int64=2) %!d(string=hai) anos
pai
achega
1439123cd4

+ 62 - 0
frameworks/JavaScript/hyperexpress/README.md

@@ -0,0 +1,62 @@
+# HyperExpress Benchmarking Test
+
+HyperExpress is a high performance Node.js webserver with a simple-to-use API powered by µWebSockets.js under the hood. (https://github.com/kartikk221/hyper-express)
+
+µWebSockets.js is a web server bypass for Node.js (https://github.com/uNetworking/uWebSockets.js)
+
+## Important Libraries
+
+The tests were run with:
+
+- [hyper-express](https://github.com/kartikk221/hyper-express)
+- [postgres](https://github.com/porsager/postgres)
+- [mysql2](https://github.com/sidorares/node-mysql2)
+- [lru-cache](https://github.com/isaacs/node-lru-cache)
+
+## Database
+
+There are individual handlers for each DB approach. The logic for each of them are found here:
+
+- [Postgres](database/postgres.js)
+- [MySQL](database/mysql.js)
+
+There are **no database endpoints** or drivers attached by default.
+
+To initialize the application with one of these, run any _one_ of the following commands:
+
+```sh
+$ DATABASE=postgres npm start
+$ DATABASE=mysql npm start
+```
+
+## Test Endpoints
+
+> Visit the test requirements [here](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview)
+
+```sh
+$ curl localhost:8080/json
+$ curl localhost:8080/plaintext
+
+# The following are only available with the DATABASE env var
+
+$ curl localhost:8080/db
+$ curl localhost:8080/fortunes
+
+$ curl localhost:8080/queries?queries=2
+$ curl localhost:8080/queries?queries=0
+$ curl localhost:8080/queries?queries=foo
+$ curl localhost:8080/queries?queries=501
+$ curl localhost:8080/queries?queries=
+
+$ curl localhost:8080/updates?queries=2
+$ curl localhost:8080/updates?queries=0
+$ curl localhost:8080/updates?queries=foo
+$ curl localhost:8080/updates?queries=501
+$ curl localhost:8080/updates?queries=
+
+$ curl localhost:8080/cached-worlds?count=2
+$ curl localhost:8080/cached-worlds?count=0
+$ curl localhost:8080/cached-worlds?count=foo
+$ curl localhost:8080/cached-worlds?count=501
+$ curl localhost:8080/cached-worlds?count=
+```

+ 134 - 0
frameworks/JavaScript/hyperexpress/app.js

@@ -0,0 +1,134 @@
+import { escape } from 'html-escaper'
+import { Server } from 'hyper-express'
+import { LRUCache } from 'lru-cache'
+import cluster, { isWorker } from 'node:cluster'
+import { maxQuery, maxRows } from './config.js'
+const { DATABASE } = process.env
+const db = DATABASE ? await import(`./database/${DATABASE}.js`) : null
+
+const generateRandomNumber = () => Math.ceil(Math.random() * maxRows)
+
+const parseQueries = (i) => Math.min(Math.max(parseInt(i, 10) || 1, 1), maxQuery)
+
+const cache = new LRUCache({
+  max: maxRows
+})
+
+const app = new Server()
+
+// use middleware to add `Server` into response header
+app.use((_request, response, next) => {
+  response.header('Server', 'hyperexpress')
+  next()
+})
+
+app.get('/plaintext', (_request, response) => {
+  response.atomic(() => {
+    response
+      .type('text')
+      .send('Hello, World!')
+  })
+})
+
+app.get('/json', (_request, response) => {
+  response.json({ message: 'Hello, World!' })
+})
+
+if (db) {
+  // populate cache
+  (async () => {
+    const worlds = await db.getAllWorlds()
+    for (let i = 0; i < worlds.length; i++) {
+      cache.set(worlds[i].id, worlds[i])
+    }
+  })()
+
+  app.get('/db', async (_request, response) => {
+    try {
+      const world = await db.find(generateRandomNumber())
+      response.json(world)
+    } catch (error) {
+      throw error
+    }
+  })
+
+  app.get('/queries', async (request, response) => {
+    try {
+      const queries = parseQueries(request.query.queries)
+      const worldPromises = []
+
+      for (let i = 0; i < queries; i++) {
+        worldPromises.push(db.find(generateRandomNumber()))
+      }
+
+      const worlds = await Promise.all(worldPromises)
+      response.json(worlds)
+    } catch (error) {
+      throw error
+    }
+  })
+
+  app.get('/updates', async (request, response) => {
+    try {
+      const queries = parseQueries(request.query.queries)
+      const worldPromises = []
+
+      for (let i = 0; i < queries; i++) {
+        worldPromises.push(db.find(generateRandomNumber()))
+      }
+
+      const worlds = await Promise.all(worldPromises)
+
+      const updatedWorlds = await Promise.all(worlds.map(async (world) => {
+        world.randomNumber = generateRandomNumber()
+        await db.update(world)
+        return world
+      }))
+      response.json(updatedWorlds)
+    } catch (error) {
+      throw error
+    }
+  })
+
+  app.get('/fortunes', async (_request, response) => {
+    try {
+      const fortunes = await db.fortunes()
+
+      fortunes.push({ id: 0, message: 'Additional fortune added at request time.' })
+
+      fortunes.sort((a, b) => a.message.localeCompare(b.message))
+
+      let i = 0, html = '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
+      for (; i < fortunes.length; i++) html += `<tr><td>${fortunes[i].id}</td><td>${escape(fortunes[i].message)}</td></tr>`
+      html += '</table></body></html>'
+
+      response.atomic(() => {
+        response
+          // .type('html')
+          .header('Content-Type', 'text/html; charset=utf-8')
+          .send(html)
+      })
+    } catch (error) {
+      throw error
+    }
+  })
+
+  app.get('/cached-worlds', async (request, response) => {
+    try {
+      const count = parseQueries(request.query.count)
+      const worlds = []
+
+      for (let i = 0; i < count; i++) {
+        worlds[i] = cache.get(generateRandomNumber())
+      }
+
+      response.json(worlds)
+    } catch (error) {
+      throw error
+    }
+  })
+}
+
+app.listen(8080).then(() => {
+  console.log(`${isWorker ? `${cluster.worker.id}: ` : ''}Successfully bound to http://0.0.0.0:8080`)
+})

+ 70 - 0
frameworks/JavaScript/hyperexpress/benchmark_config.json

@@ -0,0 +1,70 @@
+{
+  "framework": "hyperexpress",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "None",
+        "framework": "hyperexpress",
+        "language": "JavaScript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "NodeJS",
+        "webserver": "µws",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "hyperexpress",
+        "notes": "",
+        "versus": "nodejs"
+      },
+      "postgres": {
+        "db_url": "/db",
+        "fortune_url": "/fortunes",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "cached_query_url": "/cached-worlds?count=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "hyperexpress",
+        "language": "JavaScript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "NodeJS",
+        "webserver": "µws",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "hyperexpress",
+        "notes": "",
+        "versus": "nodejs"
+      },
+      "mysql": {
+        "db_url": "/db",
+        "fortune_url": "/fortunes",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "cached_query_url": "/cached-worlds?count=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "hyperexpress",
+        "language": "JavaScript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "NodeJS",
+        "webserver": "µws",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "hyperexpress",
+        "notes": "",
+        "versus": "nodejs"
+      }
+    }
+  ]
+}

+ 15 - 0
frameworks/JavaScript/hyperexpress/clustered.js

@@ -0,0 +1,15 @@
+import cluster, { isPrimary, setupPrimary, fork } from 'node:cluster'
+import { cpus } from 'node:os'
+
+if (isPrimary) {
+  setupPrimary({
+    exec: 'app.js',
+  })
+  cluster.on('exit', (worker) => {
+    console.log(`worker ${worker.process.pid} died`)
+    process.exit(1)
+  })
+  for (let i = 0; i < cpus().length; i++) {
+    fork()
+  }
+}

+ 8 - 0
frameworks/JavaScript/hyperexpress/config.js

@@ -0,0 +1,8 @@
+export const maxQuery = 500
+export const maxRows = 10000
+export const clientOpts = {
+  host: 'tfb-database',
+  user: 'benchmarkdbuser',
+  password: 'benchmarkdbpass',
+  database: 'hello_world',
+}

+ 33 - 0
frameworks/JavaScript/hyperexpress/database/mysql.js

@@ -0,0 +1,33 @@
+import { createPool, createConnection } from 'mysql2/promise'
+import { isWorker } from 'node:cluster'
+import { cpus } from 'node:os'
+import { clientOpts } from '../config.js'
+
+const client = await createConnection(clientOpts)
+
+const res = await client.query('SHOW VARIABLES LIKE "max_connections"')
+
+let maxConnections = 150
+
+if (isWorker) {
+    maxConnections = cpus().length > 2 ? Math.ceil(res[0][0].Value * 0.96 / cpus().length) : maxConnections
+}
+
+await client.end()
+
+const pool = createPool(Object.assign({ ...clientOpts }, {
+    connectionLimit: maxConnections,
+    idleTimeout: 600000
+}))
+
+const execute = async (text, values) => (await pool.execute(text, values || undefined))[0]
+
+export const fortunes = async () => execute('SELECT * FROM fortune')
+
+export const find = async (id) => execute('SELECT id, randomNumber FROM world WHERE id = ?', [id]).then(arr => arr[0])
+
+export const getAllWorlds = async () => execute('SELECT * FROM world')
+
+export const update = async (obj) => execute('UPDATE world SET randomNumber = ? WHERE id = ?', [obj.randomNumber, obj.id])
+
+await Promise.all([...Array(maxConnections).keys()].map(fortunes))

+ 14 - 0
frameworks/JavaScript/hyperexpress/database/postgres.js

@@ -0,0 +1,14 @@
+import postgres from 'postgres'
+import { clientOpts } from '../config.js'
+
+const sql = postgres(clientOpts)
+
+export const fortunes = async () => sql`SELECT * FROM fortune`
+
+export const find = async (id) => sql`SELECT id, randomNumber FROM world WHERE id = ${id}`.then((arr) => arr[0])
+
+export const getAllWorlds = async () => sql`SELECT * FROM world`
+
+export const update = async (obj) => sql`UPDATE world SET randomNumber = ${obj.randomNumber} WHERE id = ${obj.id}`
+
+await Promise.all([...Array(150).keys()].map(fortunes))

+ 18 - 0
frameworks/JavaScript/hyperexpress/hyperexpress-mysql.dockerfile

@@ -0,0 +1,18 @@
+# syntax=docker/dockerfile:1
+FROM node:18-slim
+
+WORKDIR /app
+
+COPY --chown=node:node . .
+
+ENV NODE_ENV production
+
+ENV DATABASE mysql
+
+RUN npm install
+
+USER node
+
+EXPOSE 8080
+
+CMD ["node", "clustered.js"]

+ 18 - 0
frameworks/JavaScript/hyperexpress/hyperexpress-postgres.dockerfile

@@ -0,0 +1,18 @@
+# syntax=docker/dockerfile:1
+FROM node:18-slim
+
+WORKDIR /app
+
+COPY --chown=node:node . .
+
+ENV NODE_ENV production
+
+ENV DATABASE postgres
+
+RUN npm install
+
+USER node
+
+EXPOSE 8080
+
+CMD ["node", "clustered.js"]

+ 16 - 0
frameworks/JavaScript/hyperexpress/hyperexpress.dockerfile

@@ -0,0 +1,16 @@
+# syntax=docker/dockerfile:1
+FROM node:18-slim
+
+WORKDIR /app
+
+COPY --chown=node:node . .
+
+ENV NODE_ENV production
+
+RUN npm install
+
+USER node
+
+EXPOSE 8080
+
+CMD ["node", "clustered.js"]

+ 689 - 0
frameworks/JavaScript/hyperexpress/package-lock.json

@@ -0,0 +1,689 @@
+{
+  "name": "hyperexpress",
+  "version": "0.0.1",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "version": "0.0.1",
+      "license": "MIT",
+      "dependencies": {
+        "html-escaper": "^3.0.3",
+        "hyper-express": "^6.7.1",
+        "lru-cache": "^10.0.0",
+        "mysql2": "^3.5.0",
+        "postgres": "^3.3.5"
+      }
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.2",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+      "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/busboy": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.3.2.tgz",
+      "integrity": "sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.35",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+      "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/express": {
+      "version": "4.17.17",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
+      "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "*"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "4.17.35",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz",
+      "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ=="
+    },
+    "node_modules/@types/mime": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+      "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
+    },
+    "node_modules/@types/node": {
+      "version": "16.18.38",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz",
+      "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ=="
+    },
+    "node_modules/@types/qs": {
+      "version": "6.9.7",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+      "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+      "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
+    },
+    "node_modules/@types/send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==",
+      "dependencies": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz",
+      "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/mime": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/busboy": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+      "dependencies": {
+        "streamsearch": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.16.0"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.1.tgz",
+      "integrity": "sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==",
+      "engines": {
+        "node": ">=6.6.0"
+      }
+    },
+    "node_modules/denque": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+      "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/generate-function": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+      "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+      "dependencies": {
+        "is-property": "^1.0.2"
+      }
+    },
+    "node_modules/html-escaper": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+      "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
+    },
+    "node_modules/hyper-express": {
+      "version": "6.7.1",
+      "resolved": "https://registry.npmjs.org/hyper-express/-/hyper-express-6.7.1.tgz",
+      "integrity": "sha512-QjcHXsMZ/ujoTOp6yI9r1OOw14CloghO4iHxlRyUEmTwhVW4p+2Ak8iNrpsdDUV/MczhYNGkQ5Rw3lm+yGSoeA==",
+      "dependencies": {
+        "@types/busboy": "^0.3.1",
+        "@types/express": "^4.17.13",
+        "@types/node": "^16.11.6",
+        "accepts": "^1.3.7",
+        "busboy": "^1.0.0",
+        "cookie": "^0.4.1",
+        "cookie-signature": "^1.1.0",
+        "mime-types": "^2.1.33",
+        "range-parser": "^1.2.1",
+        "type-is": "^1.6.18",
+        "typed-emitter": "^2.1.0",
+        "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.25.0"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-property": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+      "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
+    },
+    "node_modules/long": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+      "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
+    },
+    "node_modules/lru-cache": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz",
+      "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==",
+      "engines": {
+        "node": "14 || >=16.14"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mysql2": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.5.0.tgz",
+      "integrity": "sha512-WKlxITp5D8Xx8vsREf/cF0MB7QtqkAgedxtMA0iD9D0ugxmeBdph7J7xaLXdIZXWWmRyNGaCFTUiGo144XdL5g==",
+      "dependencies": {
+        "denque": "^2.1.0",
+        "generate-function": "^2.3.1",
+        "iconv-lite": "^0.6.3",
+        "long": "^5.2.1",
+        "lru-cache": "^8.0.0",
+        "named-placeholders": "^1.1.3",
+        "seq-queue": "^0.0.5",
+        "sqlstring": "^2.3.2"
+      },
+      "engines": {
+        "node": ">= 8.0"
+      }
+    },
+    "node_modules/mysql2/node_modules/lru-cache": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
+      "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
+      "engines": {
+        "node": ">=16.14"
+      }
+    },
+    "node_modules/named-placeholders": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
+      "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+      "dependencies": {
+        "lru-cache": "^7.14.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/named-placeholders/node_modules/lru-cache": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/postgres": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.5.tgz",
+      "integrity": "sha512-+JD93VELV9gHkqpV5gdL5/70HdGtEw4/XE1S4BC8f1mcPmdib3K5XsKVbnR1XcAyC41zOnifJ+9YRKxdIsXiUw==",
+      "funding": {
+        "type": "individual",
+        "url": "https://github.com/sponsors/porsager"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "node_modules/seq-queue": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+      "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+    },
+    "node_modules/sqlstring": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+      "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/streamsearch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
+      "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==",
+      "optional": true
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typed-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz",
+      "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==",
+      "dependencies": {
+        "rxjs": "*"
+      },
+      "optionalDependencies": {
+        "rxjs": "*"
+      }
+    },
+    "node_modules/uWebSockets.js": {
+      "version": "20.25.0",
+      "resolved": "git+ssh://[email protected]/uNetworking/uWebSockets.js.git#e6ecc2102d68d99dc35969b0898fbd201e0f252b"
+    }
+  },
+  "dependencies": {
+    "@types/body-parser": {
+      "version": "1.19.2",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+      "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+      "requires": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "@types/busboy": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.3.2.tgz",
+      "integrity": "sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/connect": {
+      "version": "3.4.35",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+      "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/express": {
+      "version": "4.17.17",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
+      "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
+      "requires": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "*"
+      }
+    },
+    "@types/express-serve-static-core": {
+      "version": "4.17.35",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz",
+      "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==",
+      "requires": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "@types/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ=="
+    },
+    "@types/mime": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+      "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
+    },
+    "@types/node": {
+      "version": "16.18.38",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz",
+      "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ=="
+    },
+    "@types/qs": {
+      "version": "6.9.7",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+      "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
+    },
+    "@types/range-parser": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+      "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
+    },
+    "@types/send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==",
+      "requires": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "@types/serve-static": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz",
+      "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==",
+      "requires": {
+        "@types/http-errors": "*",
+        "@types/mime": "*",
+        "@types/node": "*"
+      }
+    },
+    "accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "requires": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      }
+    },
+    "busboy": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+      "requires": {
+        "streamsearch": "^1.1.0"
+      }
+    },
+    "cookie": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+    },
+    "cookie-signature": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.1.tgz",
+      "integrity": "sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw=="
+    },
+    "denque": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+      "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
+    },
+    "generate-function": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+      "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+      "requires": {
+        "is-property": "^1.0.2"
+      }
+    },
+    "html-escaper": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+      "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
+    },
+    "hyper-express": {
+      "version": "6.7.1",
+      "resolved": "https://registry.npmjs.org/hyper-express/-/hyper-express-6.7.1.tgz",
+      "integrity": "sha512-QjcHXsMZ/ujoTOp6yI9r1OOw14CloghO4iHxlRyUEmTwhVW4p+2Ak8iNrpsdDUV/MczhYNGkQ5Rw3lm+yGSoeA==",
+      "requires": {
+        "@types/busboy": "^0.3.1",
+        "@types/express": "^4.17.13",
+        "@types/node": "^16.11.6",
+        "accepts": "^1.3.7",
+        "busboy": "^1.0.0",
+        "cookie": "^0.4.1",
+        "cookie-signature": "^1.1.0",
+        "mime-types": "^2.1.33",
+        "range-parser": "^1.2.1",
+        "type-is": "^1.6.18",
+        "typed-emitter": "^2.1.0",
+        "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.25.0"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      }
+    },
+    "is-property": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+      "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
+    },
+    "long": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+      "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
+    },
+    "lru-cache": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz",
+      "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw=="
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
+    },
+    "mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "requires": {
+        "mime-db": "1.52.0"
+      }
+    },
+    "mysql2": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.5.0.tgz",
+      "integrity": "sha512-WKlxITp5D8Xx8vsREf/cF0MB7QtqkAgedxtMA0iD9D0ugxmeBdph7J7xaLXdIZXWWmRyNGaCFTUiGo144XdL5g==",
+      "requires": {
+        "denque": "^2.1.0",
+        "generate-function": "^2.3.1",
+        "iconv-lite": "^0.6.3",
+        "long": "^5.2.1",
+        "lru-cache": "^8.0.0",
+        "named-placeholders": "^1.1.3",
+        "seq-queue": "^0.0.5",
+        "sqlstring": "^2.3.2"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "8.0.5",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
+          "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA=="
+        }
+      }
+    },
+    "named-placeholders": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
+      "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+      "requires": {
+        "lru-cache": "^7.14.1"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "7.18.3",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+          "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="
+        }
+      }
+    },
+    "negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+    },
+    "postgres": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.5.tgz",
+      "integrity": "sha512-+JD93VELV9gHkqpV5gdL5/70HdGtEw4/XE1S4BC8f1mcPmdib3K5XsKVbnR1XcAyC41zOnifJ+9YRKxdIsXiUw=="
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+    },
+    "rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "optional": true,
+      "requires": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "seq-queue": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+      "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+    },
+    "sqlstring": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+      "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="
+    },
+    "streamsearch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
+    },
+    "tslib": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
+      "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==",
+      "optional": true
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      }
+    },
+    "typed-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz",
+      "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==",
+      "requires": {
+        "rxjs": "*"
+      }
+    },
+    "uWebSockets.js": {
+      "version": "git+ssh://[email protected]/uNetworking/uWebSockets.js.git#e6ecc2102d68d99dc35969b0898fbd201e0f252b",
+      "from": "uWebSockets.js@github:uNetworking/uWebSockets.js#v20.25.0"
+    }
+  }
+}

+ 18 - 0
frameworks/JavaScript/hyperexpress/package.json

@@ -0,0 +1,18 @@
+{
+  "name": "hyperexpress",
+  "version": "0.0.1",
+  "main": "app.js",
+  "scripts": {
+    "start": "node clustered.js"
+  },
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "html-escaper": "^3.0.3",
+    "hyper-express": "^6.7.1",
+    "lru-cache": "^10.0.0",
+    "mysql2": "^3.5.0",
+    "postgres": "^3.3.5"
+  },
+  "type": "module"
+}