]> git.r.bdr.sh - rbdr/corona-regeln/commitdiff
Add project
authorRuben Beltran del Rio <redacted>
Sat, 3 Apr 2021 22:45:50 +0000 (00:45 +0200)
committerRuben Beltran del Rio <redacted>
Sat, 3 Apr 2021 22:45:50 +0000 (00:45 +0200)
.gitignore [new file with mode: 0644]
bin/corona-regeln [new file with mode: 0755]
config/index.js [new file with mode: 0644]
lib/corona-regeln.js [new file with mode: 0644]
lib/rule_generator.js [new file with mode: 0644]
package-lock.json [new file with mode: 0644]
package.json [new file with mode: 0644]
rules/business.js [new file with mode: 0644]
rules/curfew.js [new file with mode: 0644]
rules/group.js [new file with mode: 0644]
templates/index.eta [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3c3629e
--- /dev/null
@@ -0,0 +1 @@
+node_modules
diff --git a/bin/corona-regeln b/bin/corona-regeln
new file mode 100755 (executable)
index 0000000..2a0fbfb
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env node
+
+const Fastify = require('fastify')({ logger: true })
+const PointOfView = require('point-of-view');
+const Eta = require('eta');
+const { join } = require('path');
+
+const CoronaRegeln = require('../lib/corona-regeln.js');
+const Config = require('../config');
+
+Fastify.register(PointOfView, {
+  engine: {
+    eta: Eta
+  },
+  root: join(__dirname, '../templates'),
+});
+
+Fastify.get('/', async (request, reply) => {
+
+  return reply.view('/index.eta', { rules: await CoronaRegeln.run(Config) });
+});
+
+Fastify.listen(Config.port)
+  .catch((error) => {
+
+    console.error(error.stack || error.message || error);
+    process.exit(1);
+  })
diff --git a/config/index.js b/config/index.js
new file mode 100644 (file)
index 0000000..8199cbc
--- /dev/null
@@ -0,0 +1,7 @@
+const Getenv = require('getenv');
+
+module.exports = {
+
+  port: Getenv.int('CORONA_REGELN_PORT', 4949),
+  ruleCount: Getenv.int('CORONA_REGELN_RULE_COUNT', 4)
+};
diff --git a/lib/corona-regeln.js b/lib/corona-regeln.js
new file mode 100644 (file)
index 0000000..5036e47
--- /dev/null
@@ -0,0 +1,17 @@
+'use strict';
+
+const RuleGenerator = require('./rule_generator');
+
+module.exports = {
+  async run({
+    ruleCount
+  }) {
+
+    const rules = [];
+    for (let i = 0; i < ruleCount; ++i) {
+      rules.push(await RuleGenerator.generate());
+    }
+
+    return rules.sort(() => Math.random() - 0.5);
+  }
+}
diff --git a/lib/rule_generator.js b/lib/rule_generator.js
new file mode 100644 (file)
index 0000000..5db5240
--- /dev/null
@@ -0,0 +1,49 @@
+const { readdir } = require('fs/promises');
+const { join, resolve } = require('path');
+
+const internals = {
+
+  dates: [
+    'today',
+    'tomorrow',
+    'next friday',
+    'next wednesday',
+    'last thursday',
+    'last tuesday',
+    'the next day divisible by 7',
+    'this afternoon',
+    'next fiscal quarter',
+  ],
+
+  rules: null,
+  currentIndex: 0,
+
+  kRulesDirectory: resolve(process.cwd(), 'rules'),
+
+  async readRules() {
+
+    const ruleFiles = await readdir(internals.kRulesDirectory);
+    const rules = ruleFiles
+      .filter((file) => file.match(/.js$/))
+      .map((file) => require(join(internals.kRulesDirectory, file)));
+
+    internals.rules = rules.sort(() => Math.random() - 0.5)
+  }
+}
+
+module.exports = {
+  async generate() {
+
+    if (!internals.rules) {
+      await internals.readRules();
+    }
+
+    const rule = internals.rules[internals.currentIndex];
+    const time = internals.dates[Math.floor(Math.random() * internals.dates.length)];
+    internals.currentIndex = (internals.currentIndex + 1) % internals.rules.length
+    return {
+      rule: rule(),
+      time
+    }
+  }
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644 (file)
index 0000000..ea0e8b4
--- /dev/null
@@ -0,0 +1,864 @@
+{
+  "name": "corona-regeln",
+  "version": "1.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "version": "1.0.0",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "eta": "^1.12.1",
+        "fastify": "^3.14.1",
+        "getenv": "^1.0.0",
+        "point-of-view": "^4.14.0"
+      }
+    },
+    "node_modules/@fastify/forwarded": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-1.0.0.tgz",
+      "integrity": "sha512-VoO+6WD0aRz8bwgJZ8pkkxjq7o/782cQ1j945HWg0obZMgIadYW3Pew0+an+k1QL7IPZHM3db5WF6OP6x4ymMA==",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@fastify/proxy-addr": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-3.0.0.tgz",
+      "integrity": "sha512-ty7wnUd/GeSqKTC2Jozsl5xGbnxUnEFC0On2/zPv/8ixywipQmVZwuWvNGnBoitJ2wixwVqofwXNua8j6Y62lQ==",
+      "dependencies": {
+        "@fastify/forwarded": "^1.0.0",
+        "ipaddr.js": "^2.0.0"
+      }
+    },
+    "node_modules/abstract-logging": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+      "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/archy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+      "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
+    },
+    "node_modules/atomic-sleep": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+      "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/avvio": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/avvio/-/avvio-7.2.1.tgz",
+      "integrity": "sha512-b+gox68dqD6c3S3t+bZBKN6rYbVWdwpN12sHQLFTiacDT2rcq7fm07Ww+IKt/AvAkyCIe1f5ArP1bC/vAlx97A==",
+      "dependencies": {
+        "archy": "^1.0.0",
+        "debug": "^4.0.0",
+        "fastq": "^1.6.1",
+        "queue-microtask": "^1.1.2"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+      "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+      "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+      "dependencies": {
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deepmerge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+      "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/eta": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.1.tgz",
+      "integrity": "sha512-H8npoci2J/7XiPnVcCVulBSPsTNGvGaINyMjQDU8AFqp9LGsEYS88g2CiU+d01Sg44WtX7o4nb8wUJ9vnI+tiA==",
+      "engines": {
+        "node": ">=6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/eta-dev/eta?sponsor=1"
+      }
+    },
+    "node_modules/fast-decode-uri-component": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
+      "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+    },
+    "node_modules/fast-json-stringify": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-2.5.3.tgz",
+      "integrity": "sha512-wOE0YeqnhwOPqaZNvRxsgnq8Puc2vAQIlxSlcpRUInm0yf4+H/YqQ8ijBmzkUg384p0JBzjR9XBNkU5jLWSCwQ==",
+      "dependencies": {
+        "ajv": "^6.11.0",
+        "deepmerge": "^4.2.2",
+        "rfdc": "^1.2.0",
+        "string-similarity": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/fast-redact": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.0.tgz",
+      "integrity": "sha512-a/S/Hp6aoIjx7EmugtzLqXmcNsyFszqbt6qQ99BdG61QjBZF6shNis0BYR6TsZOQ1twYc0FN2Xdhwwbv6+KD0w==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/fast-safe-stringify": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
+      "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+    },
+    "node_modules/fastify": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/fastify/-/fastify-3.14.1.tgz",
+      "integrity": "sha512-9hoK1vvopsUJnUJpge90t8PZIqNQhGM54yDrd2veCZLkxh8eipnaHrXe2+f7tIt6UScUZ92JZQavxFGB4HX7xA==",
+      "dependencies": {
+        "@fastify/proxy-addr": "^3.0.0",
+        "abstract-logging": "^2.0.0",
+        "ajv": "^6.12.2",
+        "avvio": "^7.1.2",
+        "fast-json-stringify": "^2.5.2",
+        "fastify-error": "^0.3.0",
+        "fastify-warning": "^0.2.0",
+        "find-my-way": "^4.0.0",
+        "flatstr": "^1.0.12",
+        "light-my-request": "^4.2.0",
+        "pino": "^6.2.1",
+        "readable-stream": "^3.4.0",
+        "rfdc": "^1.1.4",
+        "secure-json-parse": "^2.0.0",
+        "semver": "^7.3.2",
+        "tiny-lru": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=10.16.0"
+      }
+    },
+    "node_modules/fastify-error": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/fastify-error/-/fastify-error-0.3.0.tgz",
+      "integrity": "sha512-Jm2LMTB5rsJqlS1+cmgqqM9tTs0UrlgYR7TvDT3ZgXsUI5ib1NjQlqZHf+tDK5tVPdFGwyq02wAoJtyYIRSiFA=="
+    },
+    "node_modules/fastify-plugin": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.0.tgz",
+      "integrity": "sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w=="
+    },
+    "node_modules/fastify-warning": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz",
+      "integrity": "sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw=="
+    },
+    "node_modules/fastq": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
+      "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/find-my-way": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-4.1.0.tgz",
+      "integrity": "sha512-UBD94MdO6cBi6E97XA0fBA9nwqw+xG5x1TYIPHats33gEi/kNqy7BWHAWx8QHCQQRSU5Txc0JiD8nzba39gvMQ==",
+      "dependencies": {
+        "fast-decode-uri-component": "^1.0.1",
+        "fast-deep-equal": "^3.1.3",
+        "safe-regex2": "^2.0.0",
+        "semver-store": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/flatstr": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz",
+      "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw=="
+    },
+    "node_modules/getenv": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz",
+      "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/hashlru": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz",
+      "integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A=="
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/ipaddr.js": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz",
+      "integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w==",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+    },
+    "node_modules/light-my-request": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-4.4.1.tgz",
+      "integrity": "sha512-FDNRF2mYjthIRWE7O8d/X7AzDx4otQHl4/QXbu3Q/FRwBFcgb+ZoDaUd5HwN53uQXLAiw76osN+Va0NEaOW6rQ==",
+      "dependencies": {
+        "ajv": "^6.12.2",
+        "cookie": "^0.4.0",
+        "fastify-warning": "^0.2.0",
+        "readable-stream": "^3.6.0",
+        "set-cookie-parser": "^2.4.1"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "node_modules/pino": {
+      "version": "6.11.2",
+      "resolved": "https://registry.npmjs.org/pino/-/pino-6.11.2.tgz",
+      "integrity": "sha512-bmzxwbrIPxQUlAuMkF4PWVErUGERU4z37HazlhflKFg08crsNE3fACGN6gPwg5xtKOK47Ux5cZm8YCuLV4wWJg==",
+      "dependencies": {
+        "fast-redact": "^3.0.0",
+        "fast-safe-stringify": "^2.0.7",
+        "flatstr": "^1.0.12",
+        "pino-std-serializers": "^3.1.0",
+        "quick-format-unescaped": "4.0.1",
+        "sonic-boom": "^1.0.2"
+      },
+      "bin": {
+        "pino": "bin.js"
+      }
+    },
+    "node_modules/pino-std-serializers": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
+      "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg=="
+    },
+    "node_modules/point-of-view": {
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/point-of-view/-/point-of-view-4.14.0.tgz",
+      "integrity": "sha512-NaWn3oqCWnRj4SvKtYBAwWf2MI1d9/rY5wF93PAdnYq4AzdLqi/IAwTHTf1+Ul3Toi2JMqg6caSCd+eQnwqT3Q==",
+      "dependencies": {
+        "fastify-plugin": "^3.0.0",
+        "hashlru": "^2.3.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/quick-format-unescaped": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.1.tgz",
+      "integrity": "sha512-RyYpQ6Q5/drsJyOhrWHYMWTedvjTIat+FTwv0K4yoUxzvekw2aRHMQJLlnvt8UantkZg2++bEzD9EdxXqkWf4A=="
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+      "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/ret": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz",
+      "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rfdc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/safe-regex2": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz",
+      "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==",
+      "dependencies": {
+        "ret": "~0.2.0"
+      }
+    },
+    "node_modules/secure-json-parse": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.3.2.tgz",
+      "integrity": "sha512-4oUSFU0w2d8/XQb7NO9dbMYyp/hxIwZPcZcGAlAAEziMRHs+NbUcx2Z5dda/z8o+avyQ8gpuYnTMlGh8SVwg9g=="
+    },
+    "node_modules/semver": {
+      "version": "7.3.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+      "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/semver-store": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz",
+      "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg=="
+    },
+    "node_modules/set-cookie-parser": {
+      "version": "2.4.8",
+      "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz",
+      "integrity": "sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg=="
+    },
+    "node_modules/sonic-boom": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz",
+      "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
+      "dependencies": {
+        "atomic-sleep": "^1.0.0",
+        "flatstr": "^1.0.12"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-similarity": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz",
+      "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ=="
+    },
+    "node_modules/tiny-lru": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz",
+      "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+    }
+  },
+  "dependencies": {
+    "@fastify/forwarded": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-1.0.0.tgz",
+      "integrity": "sha512-VoO+6WD0aRz8bwgJZ8pkkxjq7o/782cQ1j945HWg0obZMgIadYW3Pew0+an+k1QL7IPZHM3db5WF6OP6x4ymMA=="
+    },
+    "@fastify/proxy-addr": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-3.0.0.tgz",
+      "integrity": "sha512-ty7wnUd/GeSqKTC2Jozsl5xGbnxUnEFC0On2/zPv/8ixywipQmVZwuWvNGnBoitJ2wixwVqofwXNua8j6Y62lQ==",
+      "requires": {
+        "@fastify/forwarded": "^1.0.0",
+        "ipaddr.js": "^2.0.0"
+      }
+    },
+    "abstract-logging": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+      "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "archy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+      "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
+    },
+    "atomic-sleep": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+      "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
+    },
+    "avvio": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/avvio/-/avvio-7.2.1.tgz",
+      "integrity": "sha512-b+gox68dqD6c3S3t+bZBKN6rYbVWdwpN12sHQLFTiacDT2rcq7fm07Ww+IKt/AvAkyCIe1f5ArP1bC/vAlx97A==",
+      "requires": {
+        "archy": "^1.0.0",
+        "debug": "^4.0.0",
+        "fastq": "^1.6.1",
+        "queue-microtask": "^1.1.2"
+      }
+    },
+    "cookie": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+      "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
+    },
+    "debug": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+      "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+      "requires": {
+        "ms": "2.1.2"
+      }
+    },
+    "deepmerge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+      "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
+    },
+    "eta": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.1.tgz",
+      "integrity": "sha512-H8npoci2J/7XiPnVcCVulBSPsTNGvGaINyMjQDU8AFqp9LGsEYS88g2CiU+d01Sg44WtX7o4nb8wUJ9vnI+tiA=="
+    },
+    "fast-decode-uri-component": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
+      "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+    },
+    "fast-json-stringify": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-2.5.3.tgz",
+      "integrity": "sha512-wOE0YeqnhwOPqaZNvRxsgnq8Puc2vAQIlxSlcpRUInm0yf4+H/YqQ8ijBmzkUg384p0JBzjR9XBNkU5jLWSCwQ==",
+      "requires": {
+        "ajv": "^6.11.0",
+        "deepmerge": "^4.2.2",
+        "rfdc": "^1.2.0",
+        "string-similarity": "^4.0.1"
+      }
+    },
+    "fast-redact": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.0.tgz",
+      "integrity": "sha512-a/S/Hp6aoIjx7EmugtzLqXmcNsyFszqbt6qQ99BdG61QjBZF6shNis0BYR6TsZOQ1twYc0FN2Xdhwwbv6+KD0w=="
+    },
+    "fast-safe-stringify": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
+      "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+    },
+    "fastify": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/fastify/-/fastify-3.14.1.tgz",
+      "integrity": "sha512-9hoK1vvopsUJnUJpge90t8PZIqNQhGM54yDrd2veCZLkxh8eipnaHrXe2+f7tIt6UScUZ92JZQavxFGB4HX7xA==",
+      "requires": {
+        "@fastify/proxy-addr": "^3.0.0",
+        "abstract-logging": "^2.0.0",
+        "ajv": "^6.12.2",
+        "avvio": "^7.1.2",
+        "fast-json-stringify": "^2.5.2",
+        "fastify-error": "^0.3.0",
+        "fastify-warning": "^0.2.0",
+        "find-my-way": "^4.0.0",
+        "flatstr": "^1.0.12",
+        "light-my-request": "^4.2.0",
+        "pino": "^6.2.1",
+        "readable-stream": "^3.4.0",
+        "rfdc": "^1.1.4",
+        "secure-json-parse": "^2.0.0",
+        "semver": "^7.3.2",
+        "tiny-lru": "^7.0.0"
+      }
+    },
+    "fastify-error": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/fastify-error/-/fastify-error-0.3.0.tgz",
+      "integrity": "sha512-Jm2LMTB5rsJqlS1+cmgqqM9tTs0UrlgYR7TvDT3ZgXsUI5ib1NjQlqZHf+tDK5tVPdFGwyq02wAoJtyYIRSiFA=="
+    },
+    "fastify-plugin": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.0.tgz",
+      "integrity": "sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w=="
+    },
+    "fastify-warning": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz",
+      "integrity": "sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw=="
+    },
+    "fastq": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
+      "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "find-my-way": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-4.1.0.tgz",
+      "integrity": "sha512-UBD94MdO6cBi6E97XA0fBA9nwqw+xG5x1TYIPHats33gEi/kNqy7BWHAWx8QHCQQRSU5Txc0JiD8nzba39gvMQ==",
+      "requires": {
+        "fast-decode-uri-component": "^1.0.1",
+        "fast-deep-equal": "^3.1.3",
+        "safe-regex2": "^2.0.0",
+        "semver-store": "^0.3.0"
+      }
+    },
+    "flatstr": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz",
+      "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw=="
+    },
+    "getenv": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz",
+      "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg=="
+    },
+    "hashlru": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz",
+      "integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A=="
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ipaddr.js": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz",
+      "integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w=="
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+    },
+    "light-my-request": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-4.4.1.tgz",
+      "integrity": "sha512-FDNRF2mYjthIRWE7O8d/X7AzDx4otQHl4/QXbu3Q/FRwBFcgb+ZoDaUd5HwN53uQXLAiw76osN+Va0NEaOW6rQ==",
+      "requires": {
+        "ajv": "^6.12.2",
+        "cookie": "^0.4.0",
+        "fastify-warning": "^0.2.0",
+        "readable-stream": "^3.6.0",
+        "set-cookie-parser": "^2.4.1"
+      }
+    },
+    "lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "requires": {
+        "yallist": "^4.0.0"
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "pino": {
+      "version": "6.11.2",
+      "resolved": "https://registry.npmjs.org/pino/-/pino-6.11.2.tgz",
+      "integrity": "sha512-bmzxwbrIPxQUlAuMkF4PWVErUGERU4z37HazlhflKFg08crsNE3fACGN6gPwg5xtKOK47Ux5cZm8YCuLV4wWJg==",
+      "requires": {
+        "fast-redact": "^3.0.0",
+        "fast-safe-stringify": "^2.0.7",
+        "flatstr": "^1.0.12",
+        "pino-std-serializers": "^3.1.0",
+        "quick-format-unescaped": "4.0.1",
+        "sonic-boom": "^1.0.2"
+      }
+    },
+    "pino-std-serializers": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
+      "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg=="
+    },
+    "point-of-view": {
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/point-of-view/-/point-of-view-4.14.0.tgz",
+      "integrity": "sha512-NaWn3oqCWnRj4SvKtYBAwWf2MI1d9/rY5wF93PAdnYq4AzdLqi/IAwTHTf1+Ul3Toi2JMqg6caSCd+eQnwqT3Q==",
+      "requires": {
+        "fastify-plugin": "^3.0.0",
+        "hashlru": "^2.3.0"
+      }
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+    },
+    "queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
+    },
+    "quick-format-unescaped": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.1.tgz",
+      "integrity": "sha512-RyYpQ6Q5/drsJyOhrWHYMWTedvjTIat+FTwv0K4yoUxzvekw2aRHMQJLlnvt8UantkZg2++bEzD9EdxXqkWf4A=="
+    },
+    "readable-stream": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+      "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      }
+    },
+    "ret": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz",
+      "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ=="
+    },
+    "reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
+    },
+    "rfdc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+    },
+    "safe-regex2": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz",
+      "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==",
+      "requires": {
+        "ret": "~0.2.0"
+      }
+    },
+    "secure-json-parse": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.3.2.tgz",
+      "integrity": "sha512-4oUSFU0w2d8/XQb7NO9dbMYyp/hxIwZPcZcGAlAAEziMRHs+NbUcx2Z5dda/z8o+avyQ8gpuYnTMlGh8SVwg9g=="
+    },
+    "semver": {
+      "version": "7.3.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+      "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+      "requires": {
+        "lru-cache": "^6.0.0"
+      }
+    },
+    "semver-store": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz",
+      "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg=="
+    },
+    "set-cookie-parser": {
+      "version": "2.4.8",
+      "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz",
+      "integrity": "sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg=="
+    },
+    "sonic-boom": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz",
+      "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
+      "requires": {
+        "atomic-sleep": "^1.0.0",
+        "flatstr": "^1.0.12"
+      }
+    },
+    "string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "requires": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "string-similarity": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz",
+      "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ=="
+    },
+    "tiny-lru": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz",
+      "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow=="
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644 (file)
index 0000000..b5e618f
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "name": "corona-regeln",
+  "version": "1.0.0",
+  "description": "Generator for Corona Guidelines",
+  "main": "lib/corona-regeln.js",
+  "directories": {
+    "lib": "lib"
+  },
+  "scripts": {
+    "test": "echo 'sorry'"
+  },
+  "author": "Rubén Beltrán del Río <subscriptions@unlimited.pizza>",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "eta": "^1.12.1",
+    "fastify": "^3.14.1",
+    "getenv": "^1.0.0",
+    "point-of-view": "^4.14.0"
+  }
+}
diff --git a/rules/business.js b/rules/business.js
new file mode 100644 (file)
index 0000000..bc12866
--- /dev/null
@@ -0,0 +1,74 @@
+const internals ={
+
+  businesses: [
+    'butcher shops',
+    'plant shops',
+    'balloon factories',
+    'pet hotels',
+    'sex shops',
+    'clothes incineration facilities',
+    'city dumps',
+    'glass factories',
+    'roads',
+    'windfarm analysis consultancies',
+    'peanut depots',
+    'glue factories',
+    'bürgeramts',
+    'chiropractors',
+    'gong bath facilities',
+    'wrestling rings',
+    'cat cafés',
+    'paint your own pottery centers',
+    'dungeons',
+    'television sets',
+    'nasa offices',
+    'toilet factories',
+    'condom inspection facilities',
+    'high school newspapers',
+    'dog spas',
+    'mink breeding centers',
+    'ngos',
+    'yoga cults',
+    'mi6 safehouses',
+    'döner shops',
+    'hospitals'
+  ],
+
+  restrictions: [
+    'are required to work from home',
+    'require mandatory testing',
+    'must remain closed',
+    'must remain open',
+    'must offer 50% covid discounts',
+    'must become vaccination centers',
+    'must pay staff 3x on thursdays',
+    'can only meet with one other household',
+    'must share facilities',
+    'are exempt from mask rules',
+  ],
+
+  join(array) {
+
+    const firstPart = array.slice(0, -1).join(', ')
+    const lastPart = array.slice(-1)
+
+    const conjunction = array.length <= 1 ? '' : ', and '
+
+    return [firstPart, lastPart].join(conjunction)
+  }
+};
+
+module.exports = () => {
+
+  const businesses = internals.businesses
+    .filter(() => true)
+    .sort(() => Math.random() - 0.5)
+    .slice(0, Math.floor(Math.random() * 3) + 1)
+  const restriction = internals.restrictions[Math.floor(Math.random() * internals.restrictions.length)];
+  const text = `${internals.join(businesses)} ${restriction}`
+
+  return {
+    emoji: '🏢',
+    text: text.charAt(0).toUpperCase() + text.slice(1)
+  };
+}
diff --git a/rules/curfew.js b/rules/curfew.js
new file mode 100644 (file)
index 0000000..9fc697f
--- /dev/null
@@ -0,0 +1,69 @@
+const internals ={
+
+  activities: [
+    'sex',
+    'skinny dipping',
+    'playing chess',
+    'debating',
+    'dancing',
+    'breeding horses',
+    'laughing',
+    'sneezing',
+    'cat wrestling',
+    'ordering food',
+    'remote work',
+    'internet usage',
+    'sleeping',
+    'eating',
+    'vacuuming',
+    'using a blender',
+    'having a party',
+    'theft',
+    'engaging in economic activity',
+    'using ketchup',
+    'tailgating',
+    'embezzelment',
+    'taking the U-Bahn',
+    'cutting your hair',
+    'using recreational drugs',
+    'tipping',
+    'interpretive dancing',
+    'origami',
+    'whittling',
+    'going for a walk',
+    'working'
+  ],
+
+  restrictions: [
+    'is not allowed',
+    'is mandatory',
+    'is optional',
+    'requires a mask',
+    'is only allowed indoors',
+    'is only allowed outdoors',
+    'is only allowed in groups larger than seven',
+    'requires a license',
+    'is allowed under police supervision',
+    'is allowed following social distancing rules',
+    'is only allowed in Neukölln',
+    'is only allowed in Kreuzberg',
+    'is only allowed in Charlottenburg',
+    'is only allowed in Wedding',
+    'is only allowed in Mitte',
+    'is only allowed in Prenzlauer Berg',
+    'is only allowed in Friedrichshain',
+  ]
+};
+
+module.exports = () => {
+
+  const startTime = Math.floor(Math.random() * 23) + 1;
+  const endTtime = Math.floor(Math.random() * 23) + 1;
+  const activity = internals.activities[Math.floor(Math.random() * internals.activities.length)];
+  const restriction = internals.restrictions[Math.floor(Math.random() * internals.restrictions.length)];
+
+  return {
+    emoji: '🕚',
+    text: `From ${startTime}:00 to ${endTtime}:00 ${activity} ${restriction}`
+  };
+}
diff --git a/rules/group.js b/rules/group.js
new file mode 100644 (file)
index 0000000..909c378
--- /dev/null
@@ -0,0 +1,51 @@
+const internals ={
+
+  groups: [
+    'teenagers',
+    'influencers',
+    'vegans',
+    'tik-tok users',
+    'lactose intolerants',
+    'households',
+    'ice cream vendors',
+    'babies',
+    'police officers',
+    'drug dealers',
+    'tech workers',
+    'cryptocurrency miners',
+    'mexicans',
+    'smokers',
+    'drug users',
+    'octogenarians',
+    'public officials',
+    'atheists',
+    'god fearing parishioners',
+    'b-list actors',
+    'jugglers',
+    'seagulls',
+    'lighthouse operators',
+    'tram drivers',
+    'psychics',
+    'magicians',
+    'classically trained musicians',
+    'food delivery drivers',
+    'librarians',
+    'doctors',
+    'vaccinated cats',
+    'balloon artists'
+ ],
+};
+
+module.exports = () => {
+
+  const countA = Math.floor(Math.random() * 8) + 2;
+  const magnitude = Math.random() > 0.5 ? 99 : 5;
+  const countB = Math.floor(Math.random() * magnitude) + 1;
+  const groupA = internals.groups[Math.floor(Math.random() * internals.groups.length)];
+  const groupB = internals.groups[Math.floor(Math.random() * internals.groups.length)];
+
+  return {
+    emoji: '👨',
+    text: `No more than ${countA} ${groupA} + ${countB} ${groupB} are allowed to meet`
+  };
+}
diff --git a/templates/index.eta b/templates/index.eta
new file mode 100644 (file)
index 0000000..1fced66
--- /dev/null
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="author" content="Rubén Beltrán del Río">
+    <meta name="description" content="New corona regulations for Berlin">
+
+    <meta name="theme-color" content="#db5945">
+
+    <style>
+    body {
+      background: linear-gradient(270deg, #fff 0%, #ccc 100%);
+      font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+      padding: 2em;
+    }
+
+    h1 {
+      background: linear-gradient(104deg, rgba(229,19,34,1) 95%, rgba(229,19,34,0) 95%);
+      color: #fff;
+      padding: 0.5em 1em;
+      text-transform: uppercase;
+    }
+
+ul {
+  list-style: none;
+}
+
+li {
+  margin: 1em;
+}
+
+li h2 {
+  font-size: 1.4em;
+  margin: 0;
+  background: linear-gradient(104deg, rgba(229,19,34,1) 95%, rgba(229,19,34,0) 95%);
+  color: #fff;
+  padding: 0.2em 1em 0.2em 0.5em;
+  width: max-content;
+}
+
+li p {
+  margin: 0;
+}
+
+li span {
+  display: inline-block;
+}
+
+li .emoji {
+  position: absolute;
+  font-size: 4em;
+  margin-left: -1.1em;
+}
+
+li .text {
+  font-size: 2em;
+  padding: 0.5em 1em;
+  margin-left: 0.5em;
+  background: #fff;
+}
+    </style>
+
+    <title>New Corona Regulations for Berlin</title>
+  </head>
+  <body>
+      <h1>
+        New Corona Regulations Berlin
+      </h1>
+      <ul>
+        <% for (const rule of it.rules) { %>
+        <li>
+          <h2>Starting <%= rule.time %></h2>
+          <p>
+            <span class="emoji"><%= rule.rule.emoji %></span>
+            <span class="text"><%= rule.rule.text %></span>
+          </p>
+        </li>
+        <% } %>
+      </ul>
+  </body>
+</html>
+