diff --git a/.env.defaults b/.env.defaults index 76d680e0621515e4c2bde4491f5a781ace24cc2e..d25e90d6b7fd6325a102387c0db15430be8b11a0 100644 --- a/.env.defaults +++ b/.env.defaults @@ -3,5 +3,8 @@ CACHE_TTL=30000 PORT=8080 LOG_LEVEL=info APP_ROOT=/ +EXPOSE_API_DOCS=false +REDIS_PORT=6379 +REDIS_DB=0 REDIS_PREFIX=ui-middleware diff --git a/helm/core-ui-middleware/templates/deployment.yaml b/helm/core-ui-middleware/templates/deployment.yaml index 5bc305a3c947c6dd5ecb7f9061ebbcefb9cbc9ba..f3508e57dc81a58d91c69ac6f5beb73be735695c 100644 --- a/helm/core-ui-middleware/templates/deployment.yaml +++ b/helm/core-ui-middleware/templates/deployment.yaml @@ -46,14 +46,14 @@ spec: {{- if .Values.probe.liveness.enabled }} livenessProbe: httpGet: - path: {{ .Values.appRoot }}healthy + path: live port: http {{- omit .Values.probe.liveness "enabled" | toYaml | nindent 12 }} {{- end }} {{- if .Values.probe.readiness.enabled }} readinessProbe: httpGet: - path: {{ .Values.appRoot }}ready + path: ready port: http {{- omit .Values.probe.readiness "enabled" | toYaml | nindent 12 }} {{- end }} diff --git a/integration/caching_test.js b/integration/caching_test.js index a4b831e07dbd817b1e49f339935430fd21c4634d..e50058013aafa93b31b0415b7469b37753799d91 100644 --- a/integration/caching_test.js +++ b/integration/caching_test.js @@ -28,7 +28,7 @@ describe('File caching service', function () { }) it('caches manifest data', async function () { - const response = await request(app).get('/manifests') + const response = await request(app.server).get('/manifests') expect(response.statusCode).to.equal(200) const version = response.headers.version @@ -37,11 +37,11 @@ describe('File caching service', function () { }) it('caches html files', async function () { - const response = await request(app).get('/index.html') + const response = await request(app.server).get('/index.html') expect(response.statusCode).to.equal(200) const version = response.headers.version - const body = await client.getBuffer(getRedisKey({ version, name: '/index.html:body' })) + const body = (await client.getBuffer(getRedisKey({ version, name: '/index.html:body' }))) || '' expect(gunzipSync(body).toString()).to.equal('<html><head></head><body>it\'s me</body></html>') const meta = await client.get(getRedisKey({ version, name: '/index.html:meta' })) expect(meta).to.equal('{"headers":{"content-type":"text/html","dependencies":false,"content-encoding":"gzip"}}') @@ -52,14 +52,14 @@ describe('File caching service', function () { await client.set(getRedisKey({ version, name: '/demo.js:meta' }), '{"headers":{"content-type":"application/javascript","dependencies":false}}') await client.set(getRedisKey({ version, name: '/demo.js:body' }), 'console.log("Demo")') - const response = await request(app).get('/demo.js').set('version', version) + const response = await request(app.server).get('/demo.js').set('version', version) expect(response.statusCode).to.equal(200) // just for testing purposes, delete the keys from redis to make sure, it is served from local cache await client.del(getRedisKey({ version, name: '/demo.js:meta' })) await client.del(getRedisKey({ version, name: '/demo.js:body' })) - const response2 = await request(app).get('/demo.js').set('version', version) + const response2 = await request(app.server).get('/demo.js').set('version', version) expect(response2.statusCode).to.equal(200) }) }) diff --git a/integration/config_test.js b/integration/config_test.js index 87ed9dc24e96107034f7f9276fc056eee1fab8fa..de0cbdfd60d9d9d59431d58579916085b8089b99 100644 --- a/integration/config_test.js +++ b/integration/config_test.js @@ -11,7 +11,7 @@ describe('Configuration', function () { beforeEach(async function () { // need to set the redis-prefix. Otherwise, the bull workers will interfere - process.env.REDIS_PREFIX = Math.random() + process.env.REDIS_PREFIX = Math.random().toString() await client.flushdb() mockConfig(config = { urls: ['http://ui-server/'] }) mockFetch({ @@ -42,14 +42,14 @@ describe('Configuration', function () { // need to do this with dynamic import such that the mocked config is used await import('../src/create-queues.js').then(({ default: createQueues }) => createQueues()) - const response = await request(app).get('/meta') + const response = await request(app.server).get('/meta') expect(response.body).to.have.length(2) config.urls = [] pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), '1234') await new Promise(resolve => setTimeout(resolve, 200)) - const response2 = await request(app).get('/meta') + const response2 = await request(app.server).get('/meta') expect(response2.body).to.have.length(1) }) }) diff --git a/integration/update-version_test.js b/integration/update-version_test.js index c1bb9e3d0dc6e32a95600cf905f435ff5fdc7f41..cbd09e1dc0e7544ad3b5c8e870fef4aeaec12a5f 100644 --- a/integration/update-version_test.js +++ b/integration/update-version_test.js @@ -10,7 +10,7 @@ describe('Updates the version', function () { beforeEach(async function () { // need to set the redis-prefix. Otherwise, the bull workers will interfere - process.env.REDIS_PREFIX = Math.random() + process.env.REDIS_PREFIX = Math.random().toString() await client.flushdb() mockConfig({ urls: ['http://ui-server/'] }) mockFetch({ @@ -31,7 +31,7 @@ describe('Updates the version', function () { afterEach(async function () { td.reset() - process.env.CACHE_TTL = 30000 + process.env.CACHE_TTL = '30000' for (const queue of getQueues()) { await closeQueue(queue.name) } @@ -43,7 +43,7 @@ describe('Updates the version', function () { // need to do this with dynamic import such that the mocked config is used await import('../src/create-queues.js').then(({ default: createQueues }) => createQueues()) - const responseBeforeUpdate = await request(app).get('/index.html') + const responseBeforeUpdate = await request(app.server).get('/index.html') expect(responseBeforeUpdate.statusCode).to.equal(200) expect(responseBeforeUpdate.headers.version).to.equal('85101541') @@ -52,15 +52,15 @@ describe('Updates the version', function () { // update is executed with the second iteration expect(await getQueue('update-version').add({}).then(job => job.finished())).to.equal('85102502') - const responseAfterUpdate = await request(app).get('/index.html') + const responseAfterUpdate = await request(app.server).get('/index.html') expect(responseAfterUpdate.statusCode).to.equal(200) expect(responseAfterUpdate.headers.version).to.equal('85102502') }) it('with automatically triggered job', async function () { - process.env.CACHE_TTL = 100 + process.env.CACHE_TTL = '100' - const responseBeforeUpdate = await request(app).get('/index.html') + const responseBeforeUpdate = await request(app.server).get('/index.html') expect(responseBeforeUpdate.statusCode).to.equal(200) expect(responseBeforeUpdate.headers.version).to.equal('85101541') @@ -77,7 +77,7 @@ describe('Updates the version', function () { resolve() })) - const responseAfterUpdate = await request(app).get('/index.html') + const responseAfterUpdate = await request(app.server).get('/index.html') expect(responseAfterUpdate.statusCode).to.equal(200) expect(responseAfterUpdate.headers.version).to.equal('85102502') }) @@ -86,7 +86,7 @@ describe('Updates the version', function () { // need to do this with dynamic import such that the mocked config is used await import('../src/create-queues.js').then(({ default: createQueues }) => createQueues()) - const responseBeforeUpdate = await request(app).get('/index.html') + const responseBeforeUpdate = await request(app.server).get('/index.html') expect(responseBeforeUpdate.statusCode).to.equal(200) expect(responseBeforeUpdate.headers.version).to.equal('85101541') @@ -94,7 +94,7 @@ describe('Updates the version', function () { pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), '1234') await new Promise(resolve => setTimeout(resolve, 10)) - const responseAfterUpdate = await request(app).get('/index.html') + const responseAfterUpdate = await request(app.server).get('/index.html') expect(responseAfterUpdate.statusCode).to.equal(200) expect(responseAfterUpdate.headers.version).to.equal('1234') }) @@ -103,7 +103,7 @@ describe('Updates the version', function () { beforeEach(async function () { td.reset() // need to set the redis-prefix. Otherwise, the bull workers will interfere - process.env.REDIS_PREFIX = Math.random() + process.env.REDIS_PREFIX = Math.random().toString() await client.flushdb() // preconfigure redis await client.set(getRedisKey({ name: 'latestVersion' }), '12345') @@ -126,7 +126,7 @@ describe('Updates the version', function () { it('uses version from redis if present', async function () { app = await mockApp() - const response = await request(app).get('/index.html') + const response = await request(app.server).get('/index.html') expect(response.statusCode).to.equal(200) expect(response.headers.version).to.equal('12345') }) diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..93b387a98b34365bd37dcad44c95953c15e0fffc --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,11 @@ +{ + "typeAcquisition": { + "enable": true + }, + "compilerOptions": { + "checkJs": true, + "target": "es2022", + "module": "es2022", + "moduleResolution": "node" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 1afdc047b2f3a99b8b83469b89763e71749c9317..a6b87b7dd6181518f9994d9542fcc1190c5b96f0 100644 --- a/package.json +++ b/package.json @@ -18,17 +18,18 @@ "author": "Open-Xchange", "license": "CC-BY-NC-SA-2.5", "dependencies": { - "@cloudnative/health-connect": "^2.1.0", + "@fastify/formbody": "^7.1.0", + "@fastify/helmet": "^9.1.0", + "@fastify/swagger": "^7.4.1", "@open-xchange/logging": "^0.0.11", - "@open-xchange/metrics": "^0.0.1", "bull": "^4.8.5", "dotenv-defaults": "^5.0.2", - "express": "^4.18.1", - "helmet": "^5.1.1", + "fastify": "^4.5.3", + "fastify-metrics": "^9.2.2", + "fastify-plugin": "^4.2.1", "http-errors": "^2.0.0", "ioredis": "^5.2.2", - "js-yaml": "^4.0.0", - "swagger-ui-express": "^4.5.0" + "js-yaml": "^4.0.0" }, "devDependencies": { "autocannon": "^7.9.0", diff --git a/spec/file-depencies_test.js b/spec/file-depencies_test.js index b46e50521edb71aa9d08647b792b43f217fa6a36..e5bc4677ea4a7c9aec91a3efd89937c61283957b 100644 --- a/spec/file-depencies_test.js +++ b/spec/file-depencies_test.js @@ -38,14 +38,14 @@ describe('JS files with dependencies contain events', function () { }) it('javascript file contains dispatcher for dependencies', async function () { - const response = await request(app).get('/index.html.js') + const response = await request(app.server).get('/index.html.js') expect(response.statusCode).to.equal(200) expect(response.headers.dependencies).to.equal('main.css') expect(response.text).to.equal('console.log("this is index.html.js")\n/*injected by ui-middleware*/document.dispatchEvent(new CustomEvent("load-css",{detail:{css:["main.css"]}}))') }) it('javascript files from different versions have corrent dependencies', async function () { - const r1 = await request(app).get('/index.html.js') + const r1 = await request(app.server).get('/index.html.js') expect(r1.headers.dependencies).to.equal('main.css') mockFetchConfig['http://ui-server']['/manifest.json'] = generateSimpleViteManifest({ @@ -65,16 +65,16 @@ describe('JS files with dependencies contain events', function () { await updateVersionProcessor() }) - const r2 = await request(app).get('/index.html.js') + const r2 = await request(app.server).get('/index.html.js') expect(r2.headers.dependencies).to.equal('other.css') - const r3 = await request(app).get('/index.html.js').set('version', r1.headers.version) + const r3 = await request(app.server).get('/index.html.js').set('version', r1.headers.version) expect(r3.headers.dependencies).to.equal('main.css') - const r4 = await request(app).get('/index.html.js').set('version', r2.headers.version) + const r4 = await request(app.server).get('/index.html.js').set('version', r2.headers.version) expect(r4.headers.dependencies).to.equal('other.css') - const r5 = await request(app).get('/index.html.js') + const r5 = await request(app.server).get('/index.html.js') expect(r5.headers.dependencies).to.equal('other.css') }) }) diff --git a/spec/file_caching_test.js b/spec/file_caching_test.js index 3bbf8f2a1e4dffd09d6d23823a7aa26b97b3ce79..229cd6fefff73a7dce871be193f94223f5a5e355 100644 --- a/spec/file_caching_test.js +++ b/spec/file_caching_test.js @@ -50,7 +50,7 @@ describe('File caching service', function () { return new Response(image, { headers: { 'Content-Type': 'image/png', - 'Content-Length': imageStat.size + 'Content-Length': imageStat.size.toString() } }) } @@ -65,44 +65,44 @@ describe('File caching service', function () { }) it('serves files defined in manifest.json file', async function () { - const response = await request(app).get('/example.js') + const response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) - expect(response.headers['content-type']).to.equal('application/javascript; charset=utf-8') + expect(response.headers['content-type']).to.equal('application/javascript') expect(response.text).to.equal('this is example') // expect(response.headers['content-security-policy']).to.contain('sha256-NzZhMTE2Njc2YTgyNTZmZTdlZGVjZDU3YTNmYzRjNmM1OWZkMTI2NjRkYzZmMWM3YTkwMGU3ZTdhNDlhZmVlMwo=') - const response2 = await request(app).get('/test.txt') + const response2 = await request(app.server).get('/test.txt') expect(response2.statusCode).to.equal(200) - expect(response2.headers['content-type']).to.equal('text/plain; charset=utf-8') + expect(response2.headers['content-type']).to.equal('text/plain') expect(response2.text).to.equal('this is test') }) it('serves css files', async function () { - const response = await request(app).get('/main.css') + const response = await request(app.server).get('/main.css') expect(response.statusCode).to.equal(200) - expect(response.headers['content-type']).to.equal('text/css; charset=utf-8') + expect(response.headers['content-type']).to.equal('text/css') // expect(response.headers['content-security-policy']).to.contain('sha256-YjRiYWRlYTVhYmM5ZTZkNjE2ZGM4YjcwZWRlNzUxMmU0YjgxY2UxMWExOTI2ZjM1NzM1M2Y2MWJjNmUwMmZjMwo=') }) it('serves / as index.html', async function () { - const response = await request(app).get('/') + const response = await request(app.server).get('/') expect(response.statusCode).to.equal(200) - expect(response.headers['content-type']).to.equal('text/html; charset=utf-8') + expect(response.headers['content-type']).to.equal('text/html') expect(response.text).to.equal('<html><head></head><body>it\'s me</body></html>') }) it('directly fetches files not referenced in manifest.json files from the upstream servers', async function () { - const response = await request(app).get('/favicon.ico') + const response = await request(app.server).get('/favicon.ico') expect(response.statusCode).to.equal(200) expect(response.text).to.equal('not really a favicon, though') }) it('returns 404 if file can not be resolved', async function () { - const response = await request(app).get('/unknown-file.txt') + const response = await request(app.server).get('/unknown-file.txt') expect(response.statusCode).to.equal(404) }) it('serves binary files', async function () { - const response = await request(app).get('/image.png') + const response = await request(app.server).get('/image.png') expect(response.statusCode).to.equal(200) expect(response.body.length === imageStat.size) expect(response.body).to.deep.equal(image) @@ -121,10 +121,10 @@ describe('File caching service', function () { app = await mockApp() expect(spy.callCount).to.equal(0) - let response = await request(app).get('/example.js') + let response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(spy.callCount).to.equal(1) - response = await request(app).get('/example.js') + response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(spy.callCount).to.equal(1) }) @@ -142,14 +142,14 @@ describe('File caching service', function () { app = await mockApp() expect(spy.callCount).to.equal(0) - let response = await request(app).get('/example.js') + let response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(spy.callCount).to.equal(1) // delete file from redis await redis.client.del(`${response.headers.version}:/example.js:body`) await redis.client.del(`${response.headers.version}:/example.js:meta`) // and fetch once more - response = await request(app).get('/example.js') + response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(spy.callCount).to.equal(1) }) @@ -163,7 +163,7 @@ describe('File caching service', function () { return new Response(image, { headers: { 'Content-Type': 'image/png', - 'Content-Length': imageStat.size + 'Content-Length': imageStat.size.toString() } }) }) @@ -171,11 +171,11 @@ describe('File caching service', function () { }) app = await mockApp() expect(spy.callCount).to.equal(0) - let response = await request(app).get('/image.png') + let response = await request(app.server).get('/image.png') expect(response.statusCode).to.equal(200) expect(response.body).to.deep.equal(image) expect(spy.callCount).to.equal(1) - response = await request(app).get('/image.png') + response = await request(app.server).get('/image.png') expect(response.statusCode).to.equal(200) expect(response.body).to.deep.equal(image) expect(spy.callCount).to.equal(1) @@ -184,7 +184,7 @@ describe('File caching service', function () { it('a file is not cached again, if loaded from cache', async function () { const spy = sandbox.spy(redis.client, 'set') - let response = await request(app).get('/example.js') + let response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) // called 4 times. @@ -193,7 +193,7 @@ describe('File caching service', function () { // two times for for example.js (meta and body) expect(spy.callCount).to.equal(4) - response = await request(app).get('/example.js') + response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) // should still be called 4 times, because everything is in cache @@ -224,7 +224,7 @@ describe('File caching service', function () { }) app = await mockApp() - const response = await request(app).get('/example.js') + const response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(spy1.callCount).to.equal(0) expect(spy2.callCount).to.equal(1) @@ -245,25 +245,25 @@ describe('File caching service', function () { }) app = await mockApp() - const response1 = await request(app).get('/example.js').set('version', 1234) + const response1 = await request(app.server).get('/example.js').set('version', '1234') expect(response1.statusCode).to.equal(200) expect(response1.text).to.equal('first') - const response2 = await request(app).get('/example.js') + const response2 = await request(app.server).get('/example.js') expect(response2.statusCode).to.equal(200) expect(response2.text).to.equal('second') const latestVersion = response2.headers['latest-version'] - const response3 = await request(app).get('/example.js').set('version', 1234) + const response3 = await request(app.server).get('/example.js').set('version', '1234') expect(response3.statusCode).to.equal(200) expect(response3.text).to.equal('first') - const response4 = await request(app).get('/example.js') + const response4 = await request(app.server).get('/example.js') expect(response4.statusCode).to.equal(200) expect(response4.text).to.equal('second') - const response5 = await request(app).get('/example.js').set('version', latestVersion) + const response5 = await request(app.server).get('/example.js').set('version', latestVersion) expect(response5.statusCode).to.equal(200) expect(response5.text).to.equal('second') }) @@ -282,8 +282,8 @@ describe('File caching service', function () { expect(spy.callCount).to.equal(0) const [res1, res2] = await Promise.all([ - request(app).get('/example.js'), - request(app).get('/example.js') + request(app.server).get('/example.js'), + request(app.server).get('/example.js') ]) expect(res1.statusCode).to.equal(200) expect(res2.statusCode).to.equal(200) @@ -305,8 +305,8 @@ describe('File caching service', function () { expect(spy.callCount).to.equal(0) const [res1, res2] = await Promise.all([ - request(app).get('/manifests'), - request(app).get('/example.js') + request(app.server).get('/manifests'), + request(app.server).get('/example.js') ]) expect(res1.statusCode).to.equal(200) expect(res2.statusCode).to.equal(200) @@ -340,8 +340,8 @@ describe('File caching service', function () { expect(spy.callCount).to.equal(0) const [res1, res2] = await Promise.all([ - request(app).get('/example.js'), - request(app).get('/example.js') + request(app.server).get('/example.js'), + request(app.server).get('/example.js') ]) expect(res1.statusCode).to.equal(200) expect(res2.statusCode).to.equal(200) @@ -350,7 +350,7 @@ describe('File caching service', function () { }) it('serves files as gzip', async function () { - const response = await request(app).get('/example.js') + const response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(response.headers['content-encoding']).to.equal('gzip') diff --git a/spec/headers_test.js b/spec/headers_test.js index 3b6b124cbc2c27f16287cae435ecd09e6e8a9256..a1790b7be38fd729d163d9b9b5bcfe4e6e23c0b0 100644 --- a/spec/headers_test.js +++ b/spec/headers_test.js @@ -41,20 +41,20 @@ describe('Responses contain custom headers', function () { }) it('index.html has version', async function () { - const response = await request(app).get('/index.html') + const response = await request(app.server).get('/index.html') expect(response.statusCode).to.equal(200) expect(response.headers.version).to.equal('3215668592') }) it('serves requested version', async function () { - const response = await request(app).get('/index.html.js').set('version', '123456') + const response = await request(app.server).get('/index.html.js').set('version', '123456') expect(response.statusCode).to.equal(200) expect(response.headers.version).to.equal('123456') expect(response.headers['latest-version']).to.equal('3215668592') }) it('javascript file contains dependencies', async function () { - const response = await request(app).get('/index.html.js') + const response = await request(app.server).get('/index.html.js') expect(response.statusCode).to.equal(200) expect(response.headers.dependencies).to.equal('main.css') }) @@ -78,7 +78,7 @@ describe('Responses contain custom headers', function () { }) it('index.html has version', async function () { - const response = await request(app).get('/index.html') + const response = await request(app.server).get('/index.html') expect(response.statusCode).to.equal(200) // important here is, that it is different than in the test without meta.json expect(response.headers.version).to.equal('3961519424') @@ -105,7 +105,7 @@ describe('Responses contain custom headers', function () { }) it('index.html has version', async function () { - const response = await request(app).get('/index.html') + const response = await request(app.server).get('/index.html') expect(response.statusCode).to.equal(200) // important here is, that it is different than in the test without meta.json expect(response.headers.version).to.equal('1487554813') @@ -132,7 +132,7 @@ describe('Responses contain custom headers', function () { }) it('index.html has version', async function () { - const response = await request(app).get('/index.html') + const response = await request(app.server).get('/index.html') expect(response.statusCode).to.equal(200) // important here is, that it is different than in the test without meta.json expect(response.headers.version).to.equal('319344871') diff --git a/spec/meta_test.js b/spec/meta_test.js index de4a2a8e0f8ba958c2da4befffb0f34574b71d24..c5a0dc28046ee5e8cc2f18a15a02d99b392fc9cf 100644 --- a/spec/meta_test.js +++ b/spec/meta_test.js @@ -37,7 +37,7 @@ describe('Responses contain custom headers', function () { process.env.BUILD_TIMESTAMP = '0123456789' process.env.CI_COMMIT_SHA = '0123456789abcdef' - const response = await request(app).get('/meta') + const response = await request(app.server).get('/meta') expect(response.statusCode).to.equal(200) expect(response.body).to.deep.contain({ id: 'ui-middleware', @@ -49,7 +49,7 @@ describe('Responses contain custom headers', function () { }) it('has metadata from another ui service if available', async function () { - const response = await request(app).get('/meta') + const response = await request(app.server).get('/meta') expect(response.statusCode).to.equal(200) expect(response.body).to.deep.contain({ name: 'sample-service', @@ -58,7 +58,7 @@ describe('Responses contain custom headers', function () { }) it('has updated metadata if config is updated', async function () { - const response = await request(app).get('/meta') + const response = await request(app.server).get('/meta') expect(response.body).to.have.length(2) config.urls = [] @@ -68,7 +68,7 @@ describe('Responses contain custom headers', function () { await updateVersionProcessor() }) - const response2 = await request(app).get('/meta') + const response2 = await request(app.server).get('/meta') expect(response2.body).to.have.length(1) }) @@ -79,7 +79,7 @@ describe('Responses contain custom headers', function () { it('does not have metadata from ui service when unavailable', async function () { await import('../src/cache.js').then(({ clear }) => clear()) - const response = await request(app).get('/meta') + const response = await request(app.server).get('/meta') expect(response.statusCode).to.equal(200) expect(response.body).to.not.deep.contain({ name: 'sample-service', @@ -105,7 +105,7 @@ describe('Responses contain custom headers', function () { }) it('has metadata', async function () { - const response = await request(app).get('/meta') + const response = await request(app.server).get('/meta') expect(response.statusCode).to.equal(200) expect(response.body).to.deep.contain({ name: 'sample-service', diff --git a/spec/redirect_test.js b/spec/redirect_test.js index 5bf0aa7e1f44aa87f64db2851b8f07b558b9dbb5..8e8ca1cfa5c00b4fe313926d51af59b9c966fef3 100644 --- a/spec/redirect_test.js +++ b/spec/redirect_test.js @@ -28,19 +28,19 @@ describe('Redirects', function () { }) it('without requested location', async function () { - const response = await request(app).post('/redirect') + const response = await request(app.server).post('/redirect') expect(response.statusCode).to.equal(302) expect(response.headers.location).to.equal('../busy.html') }) it('with requested location', async function () { - const response = await request(app).post('/redirect').send('location=/appsuite/whatever/path') + const response = await request(app.server).post('/redirect').send('location=/appsuite/whatever/path') expect(response.statusCode).to.equal(302) expect(response.headers.location).to.equal('/appsuite/whatever/path') }) it('redirects /ui to /', async function () { - const response = await request(app).get('/ui') + const response = await request(app.server).get('/ui') expect(response.statusCode).to.equal(302) expect(response.headers.location).to.equal('/') }) diff --git a/spec/redis_test.js b/spec/redis_test.js index 26da95c55a36e45716e6a07517c5b9ac29988204..b1d2e46f4784b3f7775e884a27c779a2e7487a44 100644 --- a/spec/redis_test.js +++ b/spec/redis_test.js @@ -31,10 +31,10 @@ describe('Redis', function () { it('use internal cache, when redis is disabled', async function () { expect(spy.callCount).to.equal(0) - let response = await request(app).get('/example.js') + let response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(spy.callCount).to.equal(1) - response = await request(app).get('/example.js') + response = await request(app.server).get('/example.js') expect(response.statusCode).to.equal(200) expect(spy.callCount).to.equal(1) }) diff --git a/spec/server_test.js b/spec/server_test.js index b1d3c547e2fbae3a7eeb3dbaf9dc70d35c0f7b09..a2e0fc3405ac4d2ad9592e2876b65c3236cba247 100644 --- a/spec/server_test.js +++ b/spec/server_test.js @@ -23,19 +23,24 @@ describe('UI Middleware', function () { afterEach(async function () { td.reset() await new RedisMock().flushdb() - process.env.CACHE_TTL = 30000 + process.env.CACHE_TTL = '30000' }) // Some say, this is not necessary to test // But failing startups due to code errors in the probes will cause this to not work // Therefore, we should keep this - it('is healthy', async function () { - const response = await request(app).get('/healthy') + it('is ready', async function () { + const response = await request(app.server).get('/ready') + expect(response.statusCode).to.equal(200) + }) + + it('is live', async function () { + const response = await request(app.server).get('/live') expect(response.statusCode).to.equal(200) }) it('fetches manifest data', async function () { - const response = await request(app).get('/manifests') + const response = await request(app.server).get('/manifests') expect(response.statusCode).to.equal(200) expect(response.body).to.deep.equal([{ namespace: 'test', path: 'example' }]) }) @@ -51,7 +56,7 @@ describe('UI Middleware', function () { }) it('caches manifest data', async function () { - const response = await request(app).get('/manifests') + const response = await request(app.server).get('/manifests') expect(response.statusCode).to.equal(200) expect(response.body).to.deep.equal([{ namespace: 'test', path: 'example' }]) @@ -62,7 +67,7 @@ describe('UI Middleware', function () { await new Promise(resolve => setTimeout(resolve, 150)) - const response2 = await request(app).get('/manifests') + const response2 = await request(app.server).get('/manifests') expect(response2.statusCode).to.equal(200) expect(response2.body).to.deep.equal([{ namespace: 'test', path: 'example' }]) }) @@ -86,7 +91,7 @@ describe('UI Middleware', function () { }) it('can load multiple configurations', async function () { - await request(app) + await request(app.server) .get('/manifests') .then(response => { expect(response.statusCode).to.equal(200) diff --git a/spec/util.js b/spec/util.js index 78d34a7bbe59eb975d71bc4200a0a48a5ec8d406..f900351523b933452a8116a3cc0dd6ae272b0dad 100644 --- a/spec/util.js +++ b/spec/util.js @@ -45,7 +45,7 @@ export function mockRedis (data = {}, isEnabled = true) { const mock = { isReady () { return Promise.resolve() }, isEnabled () { return isEnabled }, - client: new RedisMock({ data }), + client: new RedisMock(data), pubClient: new RedisMock(), subClient: new RedisMock() } @@ -56,7 +56,8 @@ export function mockRedis (data = {}, isEnabled = true) { export async function mockApp () { register.clear() const { createApp } = await import('../src/create-app.js') - const app = createApp() - await request(app).get('/ready') + const app = await createApp() + app.listen({ port: 0 }) + await request(app.server).get('/ready') return app } diff --git a/src/config.js b/src/config.js index 233c19c4f6669922cbd655184cd108cee6b93a42..4dd7aff4518d9fc75868daddc09ebf3266932d2b 100644 --- a/src/config.js +++ b/src/config.js @@ -4,8 +4,9 @@ import { logger } from './logger.js' class Config { async load () { - const urlsSource = await fs.readFile('./config/config.yaml', 'utf8') - this._urls = yaml.load(urlsSource).baseUrls + const doc = yaml.load(await fs.readFile('./config/config.yaml', 'utf8')) + // @ts-ignore + this._urls = doc.baseUrls logger.debug('[Config] Config has been loaded') } diff --git a/src/create-app.js b/src/create-app.js index 10b21bff537aaa769154d83fec112b96101e490b..b65a6d10de0fee4fd5f754b884991ed99b4be11c 100644 --- a/src/create-app.js +++ b/src/create-app.js @@ -1,50 +1,53 @@ -import express from 'express' -import helmet from 'helmet' -import { httpLogger } from './logger.js' -import { metricsMiddleware } from '@open-xchange/metrics' -import swaggerUi from 'swagger-ui-express' +import fastify from 'fastify' +import formBodyPlugin from '@fastify/formbody' +import fastifySwagger from '@fastify/swagger' +import fastifyMetrics from 'fastify-metrics' +import helmet from '@fastify/helmet' import yaml from 'js-yaml' import fs from 'fs' -import versionMiddleware from './middlewares/version.js' -import serveFilesMiddleware from './middlewares/serve-files.js' -import finalHandlerMiddleware from './middlewares/final-handler.js' - -import health from './routes/health.js' -import manifestsRouter from './routes/manifests.js' -import metadataRouter from './routes/metadata.js' -import redirectsRouter from './routes/redirects.js' +import versionHandler from './handlers/version.js' +import healthPlugin from './plugins/health.js' +import manifestsPlugin from './plugins/manifests.js' +import metadataPlugin from './plugins/metadata.js' +import redirectsPlugin from './plugins/redirects.js' +import serveFilePlugin from './plugins/serve-files.js' +import { logger } from './logger.js' const swaggerDocument = yaml.load(fs.readFileSync('./src/swagger.yaml', 'utf8')) -const metricsMiddlewareInstance = metricsMiddleware() - -export function createApp () { - const app = express() - app.use(express.urlencoded({ extended: true })) - - // Application-level middleware - app.use(httpLogger) - app.use(helmet({ +export async function createApp (basePath) { + const app = fastify({ + logger, + connectionTimeout: 30000 + }) + await app.register(formBodyPlugin) + await app.register(helmet, { contentSecurityPolicy: false, crossOriginEmbedderPolicy: false, originAgentCluster: false, crossOriginOpenerPolicy: { policy: 'same-origin-allow-popups' } - })) - app.use(health) - app.use(metricsMiddlewareInstance) - app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)) - app.use('/swagger.json', (req, res) => res.json(swaggerDocument)) - app.timeout = 30000 - - app.use(versionMiddleware) - - app.use(manifestsRouter) - app.use(metadataRouter) - app.use(redirectsRouter) - - app.use(serveFilesMiddleware) - app.use(finalHandlerMiddleware) + }) + await app.register(healthPlugin) + await app.register(fastifyMetrics, { + routeMetrics: { + routeBlacklist: ['/ready', '/live', '/health', '/healthy', '/metrics', '/api-docs'] + } + }) + await app.register(fastifySwagger, { + routePrefix: '/api-docs', + prefix: process.env.APP_ROOT, + swagger: swaggerDocument, + exposeRoute: process.env.EXPOSE_API_DOCS === 'true' + }) + + await app.addHook('preHandler', versionHandler) + + await app.register(manifestsPlugin, { prefix: process.env.APP_ROOT }) + await app.register(metadataPlugin, { prefix: process.env.APP_ROOT }) + await app.register(redirectsPlugin, { prefix: process.env.APP_ROOT }) + + await app.register(serveFilePlugin, { prefix: process.env.APP_ROOT }) return app } diff --git a/src/handlers/version.js b/src/handlers/version.js new file mode 100644 index 0000000000000000000000000000000000000000..d02e90fee188b0403da660dd707a2727d0bb658d --- /dev/null +++ b/src/handlers/version.js @@ -0,0 +1,9 @@ +import { getLatestVersion } from '../version.js' + +export default async function versionHandler (req, reply) { + const latestVersion = await getLatestVersion() + const version = req.headers.version || latestVersion + reply.header('version', version) + reply.header('latest-version', latestVersion) + reply.version = version +} diff --git a/src/index.js b/src/index.js index a6ef6d48501eca452b25353ddf82ba4780770783..992a191719bf34ed0e3b4de07470f7ed6326c461 100644 --- a/src/index.js +++ b/src/index.js @@ -3,18 +3,14 @@ import { config } from 'dotenv-defaults' import { logger } from './logger.js' import { createApp } from './create-app.js' -import express from 'express' import createQueues from './create-queues.js' config() -const root = express() -const app = createApp() +const app = await createApp() createQueues() // Binds and listens for connections on the specified host and port -root.listen(process.env.PORT, () => { - logger.info(`[Server] ui-middleware listening on port ${process.env.PORT}, PID: ${process.pid}`) -}) +app.listen({ host: '::', port: Number(process.env.PORT) }) process.on('uncaughtException', err => { logger.error(err, 'uncaughtException') @@ -25,5 +21,3 @@ process.on('unhandledRejection', err => { logger.error(err, 'unhandledRejection') process.exit(1) }) - -root.use(process.env.APP_ROOT, app) diff --git a/src/logger.js b/src/logger.js index 50b37a87f50778a4191be0441439a003b28b67c0..e702c2b09f453521a0a98551f02f31d0a62627ef 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,8 +1,8 @@ import { config } from 'dotenv-defaults' -import { createLogger, createHttpLogger } from '@open-xchange/logging' +import { createLogger } from '@open-xchange/logging' + config() -export const httpLogger = createHttpLogger() export const logger = createLogger() -export const t0 = +new Date() -export const timeSinceStartup = () => (new Date() - t0) / 1000 +export const t0 = Date.now() +export const timeSinceStartup = () => (Date.now() - t0) / 1000 diff --git a/src/middlewares/final-handler.js b/src/middlewares/final-handler.js deleted file mode 100644 index 826a35b3e6aa81429deab49163fe5d099b52a20e..0000000000000000000000000000000000000000 --- a/src/middlewares/final-handler.js +++ /dev/null @@ -1,18 +0,0 @@ -import createError from 'http-errors' -import path from 'path' -import { logger } from '../logger.js' - -export default [function (req, res, next) { - const { body, headers } = res - if (!body) return next(createError(404, 'File does not exist.')) - - res.type(headers?.['content-type'] || path.extname(req.path) || 'html') - if (headers) res.set(headers) - - res.status(200).send(body) -}, function (err, req, res, next) { - if (!err) next() - if (err.status >= 400 && err.status < 500) logger.warn(err) - else logger.error(err) - res.status(err.status || 500).send(err.message || 'Internal server error occured') -}] diff --git a/src/middlewares/serve-files.js b/src/middlewares/serve-files.js deleted file mode 100644 index 4bae7331d8c031a5681ce367f71ecf1baf5f798b..0000000000000000000000000000000000000000 --- a/src/middlewares/serve-files.js +++ /dev/null @@ -1,20 +0,0 @@ -import { getFile } from '../files.js' -import { NotFoundError } from '../errors.js' -import createError from 'http-errors' - -export default async function (req, res, next) { - try { - if (req.method !== 'GET') return next() - const version = res.version - const path = req.path === '/' ? '/index.html' : req.path - const { body, headers } = await getFile({ version, path }) - - res.set(headers) - res.send(body) - } catch (err) { - const errors = err.errors || [err] - const fileNotFound = errors.reduce((memo, error) => memo && error instanceof NotFoundError, true) - if (fileNotFound) next(createError(404, 'File does not exist.')) - else next(err) - } -} diff --git a/src/middlewares/version.js b/src/middlewares/version.js deleted file mode 100644 index f62ecbc14a45c7dc84f35ec72a5297040b606936..0000000000000000000000000000000000000000 --- a/src/middlewares/version.js +++ /dev/null @@ -1,15 +0,0 @@ -import { getLatestVersion } from '../version.js' - -export default async (req, res, next) => { - try { - const latestVersion = await getLatestVersion() - const version = req.get('version') || latestVersion - res.setHeader('version', version) - res.setHeader('latest-version', latestVersion) - res.version = version - } catch (err) { - next(err) - } finally { - next() - } -} diff --git a/src/plugins/health.js b/src/plugins/health.js new file mode 100644 index 0000000000000000000000000000000000000000..181c3d6b2b0dc590c57a49790ef7c94f7b7a2b85 --- /dev/null +++ b/src/plugins/health.js @@ -0,0 +1,47 @@ +import * as redis from '../redis.js' +import { getLatestVersion } from '../version.js' +import { once } from '../util.js' +import { config } from '../config.js' +import { logger, timeSinceStartup } from '../logger.js' +import createError from 'http-errors' + +const checkRedis = () => { + if (redis.isEnabled()) { + return redis.isReady() + } +} + +const checkLatestVersion = once(async function () { + // config is required before the first version check + await config.load() + await getLatestVersion() + logger.info(`[Health] Check latest version on startup. Time since startup: ${timeSinceStartup()}`) +}) + +async function live () {} + +async function ready () { + await Promise.all([ + checkLatestVersion(), + checkRedis() + ]) +} + +export default async function healthPlugin (fastify, options) { + fastify.decorate('probe', async (reply, func) => { + try { + await func() + reply.send('') + } catch (err) { + throw createError(503) + } + }) + + fastify.get('/live', async (req, reply) => { + await fastify.probe(reply, live) + }) + + fastify.get('/ready', async (req, reply) => { + await fastify.probe(reply, ready) + }) +} diff --git a/src/plugins/manifests.js b/src/plugins/manifests.js new file mode 100644 index 0000000000000000000000000000000000000000..0f074c1394f60557bcce9eabba1a508cd41fe0c1 --- /dev/null +++ b/src/plugins/manifests.js @@ -0,0 +1,8 @@ +import { getOxManifests } from '../manifests.js' + +export default async function manifestsPlugin (fastify, options) { + fastify.get('/manifests', async (req, res) => { + if (res.body) return + res.send(await getOxManifests({ version: res.version })) + }) +} diff --git a/src/plugins/metadata.js b/src/plugins/metadata.js new file mode 100644 index 0000000000000000000000000000000000000000..81917880dde1b1fc54bdc211a0d7102e24ea964a --- /dev/null +++ b/src/plugins/metadata.js @@ -0,0 +1,7 @@ +import { getMergedMetadata } from '../meta.js' + +export default async function metadataPlugin (fastify, options) { + fastify.get('/meta', async (req, res) => { + res.send(await getMergedMetadata({ version: res.version })) + }) +} diff --git a/src/plugins/redirects.js b/src/plugins/redirects.js new file mode 100644 index 0000000000000000000000000000000000000000..d057a2b19b6a82921c5b17cf2e4b5e02eb4fbf9a --- /dev/null +++ b/src/plugins/redirects.js @@ -0,0 +1,10 @@ +export default async function redirectsPlugin (fastify, options) { + fastify.get('/ui', async (req, res) => { + res.redirect(process.env.APP_ROOT) + }) + + fastify.post('/redirect', async (req, res) => { + const location = req.body?.location || '../busy.html' + res.redirect(location) + }) +} diff --git a/src/plugins/serve-files.js b/src/plugins/serve-files.js new file mode 100644 index 0000000000000000000000000000000000000000..f6b9cb933ce416caf0c6f830c431ab5d6bc52e62 --- /dev/null +++ b/src/plugins/serve-files.js @@ -0,0 +1,24 @@ +import { getFile } from '../files.js' +import { NotFoundError } from '../errors.js' +import createError from 'http-errors' + +export default async function serveFilePlugin (fastify, options) { + fastify.get('*', async (req, reply) => { + try { + const version = reply.version + const url = req.url.substr((options.prefix || '/').length - 1) + const path = url === '/' ? '/index.html' : url + const { body, headers } = await getFile({ version, path }) + + Object.entries(headers).forEach(([key, value]) => { + reply.header(key, value) + }) + reply.send(body) + } catch (err) { + const errors = err.errors || [err] + const fileNotFound = errors.reduce((memo, error) => memo && error instanceof NotFoundError, true) + if (fileNotFound) throw createError(404, 'File does not exist.') + throw err + } + }) +} diff --git a/src/redis.js b/src/redis.js index 1e5e2abd497e87ffb6052c63862f6295a9c30469..a26432a97be863c7e37444cd035b0c578842b69c 100644 --- a/src/redis.js +++ b/src/redis.js @@ -6,7 +6,16 @@ const commonQueueOptions = { enableReadyCheck: false, maxRetriesPerRequest: null const createClient = (type, options = {}) => { if (!isEnabled()) { - return new Proxy({}, { + return new Proxy({ + getBuffer () {}, + get () {}, + set () {}, + del () {}, + flushdb () {}, + status: '', + duplicate () { return new Redis() }, + publish () {} + }, { get () { throw new Error('Redis is disabled. Check for redis.isEnabled()') } @@ -15,8 +24,8 @@ const createClient = (type, options = {}) => { const client = new Redis({ host: process.env.REDIS_HOST, - port: process.env.REDIS_PORT || 6379, - db: process.env.REDIS_DB, + port: Number(process.env.REDIS_PORT), + db: Number(process.env.REDIS_DB), password: process.env.REDIS_PASSWORD, ...options }) @@ -43,12 +52,13 @@ const queues = {} export function getQueue (name) { if (queues[name]) return queues[name] + // @ts-ignore return (queues[name] = new Queue(name, { prefix: process.env.REDIS_PREFIX, createClient: function (type) { switch (type) { case 'client': - return client + return client.duplicate() case 'subscriber': return subClient.duplicate() default: diff --git a/src/routes/health.js b/src/routes/health.js deleted file mode 100644 index 6920cd0624596e30cdeb6f429f9857e52c5dbe6e..0000000000000000000000000000000000000000 --- a/src/routes/health.js +++ /dev/null @@ -1,29 +0,0 @@ -import { Router } from 'express' -import health from '@cloudnative/health-connect' -import * as redis from '../redis.js' -import { getLatestVersion } from '../version.js' -import { once } from '../util.js' -import { config } from '../config.js' -import { logger, timeSinceStartup } from '../logger.js' -const router = Router() -const healthCheck = new health.HealthChecker() - -if (redis.isEnabled()) { - const redisReady = new health.ReadinessCheck('Redis ready', redis.isReady) - healthCheck.registerReadinessCheck(redisReady) -} - -const checkLatestVersion = new health.StartupCheck('check latest version', once(async function () { - // config is required before the first version check - await config.load() - await getLatestVersion() - logger.info(`[Health] Check latest version on startup. Time since startup: ${timeSinceStartup()}`) -})) - -healthCheck.registerStartupCheck(checkLatestVersion) - -router.use('/live', health.LivenessEndpoint(healthCheck)) -router.use('/ready', health.ReadinessEndpoint(healthCheck)) -router.use('/healthy', health.HealthEndpoint(healthCheck)) - -export default router diff --git a/src/routes/manifests.js b/src/routes/manifests.js deleted file mode 100644 index 5d75dd2bbf4ac6a019d766c8dcb6f8ad72be8609..0000000000000000000000000000000000000000 --- a/src/routes/manifests.js +++ /dev/null @@ -1,15 +0,0 @@ -import { Router } from 'express' -import { getOxManifests } from '../manifests.js' - -const router = new Router() - -router.get('/manifests', async function (req, res, next) { - try { - if (res.body) return - res.send(await getOxManifests({ version: res.version })) - } catch (err) { - next(err) - } -}) - -export default router diff --git a/src/routes/metadata.js b/src/routes/metadata.js deleted file mode 100644 index ebe28e7628bc47308d93ed9f05c87774db3bf9b3..0000000000000000000000000000000000000000 --- a/src/routes/metadata.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Router } from 'express' -import { getMergedMetadata } from '../meta.js' - -const router = new Router() - -router.get('/meta', async (req, res, next) => { - try { - res.send(await getMergedMetadata({ version: res.version })) - } catch (err) { - next(err) - } -}) - -export default router diff --git a/src/routes/redirects.js b/src/routes/redirects.js deleted file mode 100644 index 8c73035451153efe7c26525cc4b006250e8ab793..0000000000000000000000000000000000000000 --- a/src/routes/redirects.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Router } from 'express' - -const router = new Router() - -// backwards compatibility for 7.10.x -// this should hopefully be resolved with an ingress -// or proper config. But is used to be safe on all ends -router.get('/ui', async (req, res, next) => { - res.redirect(process.env.APP_ROOT) -}) - -router.post('/redirect', (req, res, next) => { - const location = req.body.location || '../busy.html' - res.redirect(location) -}) - -export default router diff --git a/src/util.js b/src/util.js index c9eccb5cfb80a385051185f0c9c324ff42f4771b..1691bed8546cdb1680301fc4a1191f878ebcb964 100644 --- a/src/util.js +++ b/src/util.js @@ -55,7 +55,7 @@ export function viteToOxManifest (viteManifests) { .flat() } -export function getRedisKey ({ version, name }) { +export function getRedisKey ({ version = undefined, name }) { if (version && name) return `${process.env.REDIS_PREFIX}:${version}:${name}` return `${process.env.REDIS_PREFIX}:${name}` } @@ -71,4 +71,4 @@ export function once (fn, context) { return res } } -export const t0 = +new Date() +export const t0 = Date.now() diff --git a/src/version.js b/src/version.js index 87c7eb71a2a624bdd58a13da197a3296d47eb61f..9c500ebea7098ddd26d8242f13473039f582e8c8 100644 --- a/src/version.js +++ b/src/version.js @@ -40,6 +40,7 @@ export async function getLatestVersion () { } } + await config.load() const version = await fetchLatestVersion() logger.info(`[Version] Fetched initial version: '${version}'`) diff --git a/yarn.lock b/yarn.lock index 74d215a8861677cb2a04f46335fe8c77859a24a5..9dd7cb3c421c61ce1c632def5e1a21cac989c323 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,18 +7,6 @@ resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.19.23.tgz#7fccae28d0a2692869f1d1219d36093bc24d5e72" integrity sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw== -"@cloudnative/health-connect@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@cloudnative/health-connect/-/health-connect-2.1.0.tgz#f6cc0d4d2ba5c7e91b7b53c28ce0ca48eb9cc1cd" - integrity sha512-GieeiusY64i1Q5LdAf70n5W08MF5uom3qdVvnwIed/asBqpNPv7hlC6FGSH20lTYvyujF3wV5oBDcTa4SS/cbA== - dependencies: - "@cloudnative/health" "^2.1.1" - -"@cloudnative/health@^2.1.1": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@cloudnative/health/-/health-2.1.2.tgz#337a4d23ac6872ec439e21aec2499785d7a27fd4" - integrity sha512-mEQdbj9dM4KMClS358MCzbqXUmj+Vw5snjDb5bXdaf1sZvVu7+3UqR4HaG4RoNkNUwe1yjfIuengdyWp9quF1A== - "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -39,6 +27,77 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@fastify/accept-negotiator@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-1.0.0.tgz#f0e73a3f8c6ba739d66a629b386c838889ec7a23" + integrity sha512-4R/N2KfYeld7A5LGkai+iUFMahXcxxYbDp+XS2B1yuL3cdmZLJ9TlCnNzT3q5xFTqsYm0GPpinLUwfSwjcVjyA== + +"@fastify/ajv-compiler@^3.1.1": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@fastify/ajv-compiler/-/ajv-compiler-3.2.0.tgz#a165ffb877fe239571a68f7b22143034176dcb8a" + integrity sha512-JrqgKmZoh1AJojDZk699DupQ9+tz5gSy7/w+5DrkXy5whM5IcqdV3SjG5qnOqgVJT1nPtUMDY0xYus2j6vwJiw== + dependencies: + ajv "^8.10.0" + ajv-formats "^2.1.1" + fast-uri "^2.0.0" + +"@fastify/deepmerge@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@fastify/deepmerge/-/deepmerge-1.1.0.tgz#91f0a5a27034ff76b7bece63a5906894940ace82" + integrity sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g== + +"@fastify/error@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@fastify/error/-/error-3.0.0.tgz#bfcb7b33cec0196413083a91ef2edc7b2c88455b" + integrity sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg== + +"@fastify/fast-json-stringify-compiler@^4.0.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.1.0.tgz#ebf657ce4ec88e27ba311f7560eaa0b37de8719d" + integrity sha512-cTKBV2J9+u6VaKDhX7HepSfPSzw+F+TSd+k0wzifj4rG+4E5PjSFJCk19P8R6tr/72cuzgGd+mbB3jFT6lvAgw== + dependencies: + fast-json-stringify "^5.0.0" + +"@fastify/formbody@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@fastify/formbody/-/formbody-7.1.0.tgz#be1963dc439483b5d49f14853243bdf87e7a3e3a" + integrity sha512-jiMb1Ie7APs7mLBuLeBGoMD0mhMGuQECjBhhPYxoFcPaORD10Ocjxwg2BWTkBgg1PYxJWXkVxFxoyBLXGrJDdA== + dependencies: + fastify-plugin "^4.0.0" + +"@fastify/helmet@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@fastify/helmet/-/helmet-9.1.0.tgz#6a87b35c8d03a826ee052e306d2d2c81b49088bc" + integrity sha512-wAtu/38t90vH46GdLM6pVi8tM1HZXb6PRdLTNBo9pudSfSw5i5Ogm8Hq1Nx337WjgSOeU6MefaTd+1avlOlCDQ== + dependencies: + fastify-plugin "^3.0.0" + helmet "^5.0.1" + +"@fastify/static@^6.0.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@fastify/static/-/static-6.5.0.tgz#004fdb487ddf0ee5fdf7541e56bfcb493d7a1060" + integrity sha512-WEk6iqgejA6ivjkvbJ47A+uMci225z5lZwLXCXZS3ZYR/kYje1gzzarkKKGL6TWpBw6smkOzxA7dfEoY0347Nw== + dependencies: + "@fastify/accept-negotiator" "^1.0.0" + content-disposition "^0.5.3" + fastify-plugin "^4.0.0" + glob "^8.0.1" + p-limit "^3.1.0" + readable-stream "^4.0.0" + send "^0.18.0" + +"@fastify/swagger@^7.4.1": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@fastify/swagger/-/swagger-7.4.1.tgz#7149a5db69f03a7d43b74ba891fda8ce35d761a0" + integrity sha512-SN3sjz4dsb6jyochD6RNXG1GYBmt45YCIiQW3YjXF1Q8hP4aYQ7pVcRH2prcm12G6MJ4fGq58ywqh3prrO/Ocg== + dependencies: + "@fastify/static" "^6.0.0" + fastify-plugin "^3.0.1" + js-yaml "^4.1.0" + json-schema-resolver "^1.3.0" + openapi-types "^11.0.0" + rfdc "^1.3.0" + "@humanwhocodes/config-array@^0.10.4": version "0.10.4" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" @@ -128,14 +187,6 @@ pino-http "^6.5.0" pino-pretty "^7.4.0" -"@open-xchange/metrics@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@open-xchange/metrics/-/metrics-0.0.1.tgz#2531f053137077e1c89ad1fc42d3ed59df68f2ff" - integrity sha512-Z2gH3USWzXDvZwxhecfZOOT1Ixse7n1aiq1Q87pFAQAyGVZeUJOIgIeCpU2m39SXFlI0Z2hwepa6Eapa+Ujj8g== - dependencies: - express-prom-bundle "^6.4.1" - prom-client "^14.0.1" - "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -179,13 +230,17 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" + event-target-shim "^5.0.0" + +abstract-logging@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" + integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA== acorn-jsx@^5.3.2: version "5.3.2" @@ -205,6 +260,13 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -215,6 +277,16 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.10.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -264,6 +336,11 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== + argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -279,11 +356,6 @@ args@^5.0.1: leven "2.1.0" mri "1.1.4" -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - array-includes@^3.1.4: version "3.1.5" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" @@ -364,6 +436,15 @@ autocannon@^7.9.0: subarg "^1.0.0" timestring "^6.0.0" +avvio@^8.1.3: + version "8.2.0" + resolved "https://registry.yarnpkg.com/avvio/-/avvio-8.2.0.tgz#aff28b0266617bf07ffc1c2d5f4220c3663ce1c2" + integrity sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg== + dependencies: + archy "^1.0.0" + debug "^4.0.0" + fastq "^1.6.1" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -384,24 +465,6 @@ bintrees@1.0.2: resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== -body-parser@1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" - integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.10.3" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -451,11 +514,6 @@ bull@^4.8.5: semver "^7.3.2" uuid "^8.3.0" -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -641,24 +699,14 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -content-disposition@0.5.4: +content-disposition@^0.5.3: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.5.0: +cookie@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== @@ -701,7 +749,7 @@ debug@2.6.9, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4.3.4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@4.3.4, debug@^4.0.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1122,6 +1170,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + execa@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" @@ -1137,51 +1190,6 @@ execa@^6.1.0: signal-exit "^3.0.7" strip-final-newline "^3.0.0" -express-prom-bundle@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/express-prom-bundle/-/express-prom-bundle-6.5.0.tgz#cdf28b29907618cae933ed14d5b727f7404e810b" - integrity sha512-paFAm0FK7TV1Ln6Blh9edDt2mJ4Pk6Py/fjhZDMcoMHENYryBjCpnXDXuCu8NE1kkvp58IrPcAAkNeNqdvZnnw== - dependencies: - on-finished "^2.3.0" - url-value-parser "^2.0.0" - -express@^4.18.1: - version "4.18.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" - integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.0" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.10.3" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1203,6 +1211,17 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stringify@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-5.1.0.tgz#dc184049d7eed4f61e34f65e97c0763fd043977f" + integrity sha512-IybGfbUc1DQgyrp9Myhwlr1Z5vjV37mBkdgcbuvsvUxv5fayG+cHlTQQpXH9nMwUPgp+5Y3RT7QDgx5zJ9NS3A== + dependencies: + "@fastify/deepmerge" "^1.0.0" + ajv "^8.10.0" + ajv-formats "^2.1.1" + fast-uri "^2.1.0" + rfdc "^1.2.0" + fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -1213,11 +1232,21 @@ fast-redact@^3.0.0: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.1.tgz#790fcff8f808c2e12fabbfb2be5cb2deda448fa0" integrity sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A== +fast-redact@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" + integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== + fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-uri@^2.0.0, fast-uri@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-2.1.0.tgz#9279432d6b53675c90116b947ed2bbba582d6fb5" + integrity sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA== + fast-url-parser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" @@ -1225,7 +1254,45 @@ fast-url-parser@^1.1.3: dependencies: punycode "^1.3.2" -fastq@^1.6.0: +fastify-metrics@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/fastify-metrics/-/fastify-metrics-9.2.2.tgz#11a4d6a288db0d365659c809efc3508fa6a36d33" + integrity sha512-67PsMy33zCvZk9juIY79YXO/dSLnVhYAV4510uiHfoa/z1VwTIoSkbsEa17JB+VL+KNW3seaSCDacjxOO/OINg== + dependencies: + fastify-plugin "^4.2.0" + prom-client "^14.0.1" + +fastify-plugin@^3.0.0, fastify-plugin@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-3.0.1.tgz#79e84c29f401020f38b524f59f2402103fd21ed2" + integrity sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA== + +fastify-plugin@^4.0.0, fastify-plugin@^4.2.0, fastify-plugin@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-4.2.1.tgz#4b80020957938dbc44b8ad4a898fd8bcfbab3f65" + integrity sha512-dlGKiwLzRBKkEf5J5ho0uAD/Jdv8GQVUbriB3tAX3ehRUXE4gTV3lRd5inEg9li1aLzb0EGj8y2K4/8g1TN06g== + +fastify@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/fastify/-/fastify-4.5.3.tgz#df4f00347f06e8f3d6ab35788d2d140668c2533c" + integrity sha512-Q8Zvkmg7GnioMCDX1jT2Q7iRqjywlnDZ1735D2Ipf7ashCM/3/bqPKv2Jo1ZF2iDExct2eP1C/tdhcj0GG/OuQ== + dependencies: + "@fastify/ajv-compiler" "^3.1.1" + "@fastify/error" "^3.0.0" + "@fastify/fast-json-stringify-compiler" "^4.0.0" + abstract-logging "^2.0.1" + avvio "^8.1.3" + find-my-way "^7.0.0" + light-my-request "^5.5.1" + pino "^8.0.0" + process-warning "^2.0.0" + proxy-addr "^2.0.7" + rfdc "^1.3.0" + secure-json-parse "^2.4.0" + semver "^7.3.7" + tiny-lru "^8.0.2" + +fastq@^1.6.0, fastq@^1.6.1: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== @@ -1260,18 +1327,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +find-my-way@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/find-my-way/-/find-my-way-7.0.1.tgz#079d6a8b474754e073c75778da678f59dedd620f" + integrity sha512-w05SaOPg54KqBof/RDA+75n1R48V7ZZNPL3nR17jJJs5dgZpR3ivfrMWOyx7BVFQgCLhYRG05hfgFCohYvSUXA== dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" + fast-deep-equal "^3.1.3" + safe-regex2 "^2.0.0" find-up@5.0.0, find-up@^5.0.0: version "5.0.0" @@ -1438,6 +1500,17 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" + integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + globals@^13.15.0: version "13.17.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" @@ -1527,7 +1600,7 @@ he@1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -helmet@^5.1.1: +helmet@^5.0.1: version "5.1.1" resolved "https://registry.yarnpkg.com/helmet/-/helmet-5.1.1.tgz#609823c5c2e78aea62dd9afc8f544ca409da5e85" integrity sha512-/yX0oVZBggA9cLJh8aw3PPCfedBnbd7J2aowjzsaWwZh7/UFY0nccn/aHAggIgWUFfnykX8GKd3a1pSbrmlcVQ== @@ -1571,13 +1644,6 @@ hyperid@^3.0.0: uuid "^8.3.2" uuid-parse "^1.1.0" -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -1838,11 +1904,25 @@ js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +json-schema-resolver@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/json-schema-resolver/-/json-schema-resolver-1.3.0.tgz#0840864b06780363d31fb03cdfae5047e2f81fbb" + integrity sha512-EX7W1r8aZ/T3j8GbbBxPXi60bnsELfT90OiA1QrbGMvwzVSbyMNOAzvMFcFb8m7gKCXZLJpGe+cJOvWgoFl29A== + dependencies: + debug "^4.1.1" + rfdc "^1.1.4" + uri-js "^4.2.2" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -1873,6 +1953,15 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +light-my-request@^5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/light-my-request/-/light-my-request-5.5.1.tgz#566d90928b9b960d44b6b2b74e072eec1f7015e4" + integrity sha512-Zd4oZjF7axSyc5rYQsbB0qsgY4LFFviZSbEywxf7Vi5UE3y3c7tYF/GeheQjBNYY+pQ55BF8UGGJTjneoxOS1w== + dependencies: + cookie "^0.5.0" + process-warning "^2.0.0" + set-cookie-parser "^2.4.1" + lilconfig@2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" @@ -2000,16 +2089,6 @@ manage-path@^2.0.0: resolved "https://registry.yarnpkg.com/manage-path/-/manage-path-2.0.0.tgz#f4cf8457b926eeee2a83b173501414bc76eb9597" integrity sha512-NJhyB+PJYTpxhxZJ3lecIGgh4kwIY2RAh44XvAz9UlqthlQwtPBf62uBVR8XaD8CRuSjQ6TnZH2lNJkbLPZM2A== -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -2020,7 +2099,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@^1.1.2, methods@~1.1.2: +methods@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== @@ -2038,7 +2117,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -2079,6 +2158,13 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" + integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -2163,11 +2249,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - nise@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.1.tgz#ac4237e0d785ecfcb83e20f389185975da5c31f3" @@ -2253,7 +2334,12 @@ on-exit-leak-free@^0.2.0: resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== -on-finished@2.4.1, on-finished@^2.3.0: +on-exit-leak-free@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz#5c703c968f7e7f851885f6459bf8a8a57edc9cc4" + integrity sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w== + +on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -2286,6 +2372,11 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" +openapi-types@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-11.1.0.tgz#037969f3dfa5999423ee33bf889fb0d12984277e" + integrity sha512-ZW+Jf12flFF6DXSij8DGL3svDA4RtSyHXjC/xB/JAh18gg3uVfVIFLvCfScUMowrpvlkxsMMbErakbth2g3/iQ== + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -2308,7 +2399,7 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -2353,11 +2444,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2383,11 +2469,6 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -2423,6 +2504,14 @@ pino-abstract-transport@^0.5.0, pino-abstract-transport@v0.5.0: duplexify "^4.1.2" split2 "^4.0.0" +pino-abstract-transport@v1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" + integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + pino-http@^6.5.0: version "6.6.0" resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-6.6.0.tgz#d0a1deacada8c93327fdaa48f5bdc94bc43d3407" @@ -2462,6 +2551,11 @@ pino-std-serializers@^5.0.0: resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz#31b141155d6520967c5ec72944d08fb45c490fd3" integrity sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A== +pino-std-serializers@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz#4c20928a1bafca122fdc2a7a4a171ca1c5f9c526" + integrity sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ== + pino@^7.5.0, pino@^7.6.2: version "7.11.0" resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" @@ -2479,6 +2573,23 @@ pino@^7.5.0, pino@^7.6.2: sonic-boom "^2.2.1" thread-stream "^0.15.1" +pino@^8.0.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.4.2.tgz#5de76e81b36e173d74244e0af4543e7ae241dbfd" + integrity sha512-PlXDeGhJZfAuVay+wtlS02s5j8uisQveZExYdAm9MwwxUQSz9R7Q78XtjM2tTa4sa5KJmygimZjZxXXuHgV6ew== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport v1.0.0 + pino-std-serializers "^6.0.0" + process-warning "^2.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^3.1.0" + thread-stream "^2.0.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -2494,6 +2605,11 @@ process-warning@^1.0.0: resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== +process-warning@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.0.0.tgz#341dbeaac985b90a04ebcd844d50097c7737b2ee" + integrity sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww== + progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -2506,7 +2622,7 @@ prom-client@^14.0.1: dependencies: tdigest "^0.1.1" -proxy-addr@~2.0.7: +proxy-addr@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -2537,13 +2653,6 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.10.3: - version "6.10.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" - integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== - dependencies: - side-channel "^1.0.4" - qs@6.9.3: version "6.9.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" @@ -2591,16 +2700,6 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - readable-stream@^3.1.1, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -2610,6 +2709,13 @@ readable-stream@^3.1.1, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.1.0.tgz#280d0a29f559d3fb684a277254e02b6f61ae0631" + integrity sha512-sVisi3+P2lJ2t0BPbpK629j8wRW06yKGJUcaLAGXPAUhyUxVJm7VsCTit1PFgT4JHUDMrGNR+ZjSKpzGaRF3zw== + dependencies: + abort-controller "^3.0.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -2627,6 +2733,11 @@ real-require@^0.1.0: resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + redis-commands@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" @@ -2668,6 +2779,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -2690,6 +2806,11 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +ret@~0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c" + integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ== + retimer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/retimer/-/retimer-3.0.0.tgz#98b751b1feaf1af13eb0228f8ea68b8f9da530df" @@ -2700,7 +2821,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfdc@^1.3.0: +rfdc@^1.1.4, rfdc@^1.2.0, rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== @@ -2731,16 +2852,18 @@ safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-stable-stringify@^2.1.0: +safe-regex2@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/safe-regex2/-/safe-regex2-2.0.0.tgz#b287524c397c7a2994470367e0185e1916b1f5b9" + integrity sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ== + dependencies: + ret "~0.2.0" + +safe-stable-stringify@^2.1.0, safe-stable-stringify@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73" integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg== -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - secure-json-parse@^2.4.0: version "2.5.0" resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.5.0.tgz#f929829df2adc7ccfb53703569894d051493a6ac" @@ -2763,7 +2886,7 @@ semver@~7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -send@0.18.0: +send@^0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== @@ -2789,15 +2912,10 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" +set-cookie-parser@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz#ddd3e9a566b0e8e0862aca974a6ac0e01349430b" + integrity sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ== setprototypeof@1.2.0: version "1.2.0" @@ -2887,6 +3005,13 @@ sonic-boom@^2.2.0, sonic-boom@^2.2.1: dependencies: atomic-sleep "^1.0.0" +sonic-boom@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.2.0.tgz#ce9f2de7557e68be2e52c8df6d9b052e7d348143" + integrity sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA== + dependencies: + atomic-sleep "^1.0.0" + split2@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" @@ -3055,18 +3180,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -swagger-ui-dist@>=4.11.0: - version "4.13.2" - resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-4.13.2.tgz#fb814efd51bf06aa8630c0a0af6e3caa48ac6552" - integrity sha512-jHL6UyIYpvEI7NsuWd0R3hJaPQTg6Oo4qSBo+oVfOEkv6rrQm/475RGSMmZgV6ajp+Sgrp9CqrDjQYAgQqiv1A== - -swagger-ui-express@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-4.5.0.tgz#feb1314627092eb9c7e6b65ee018927011445530" - integrity sha512-DHk3zFvsxrkcnurGvQlAcLuTDacAVN1JHKDgcba/gr2NFRE4HGwP1YeHIXMiGznkWR4AeS7X5vEblNn4QljuNA== - dependencies: - swagger-ui-dist ">=4.11.0" - tdigest@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" @@ -3101,6 +3214,13 @@ thread-stream@^0.15.1: dependencies: real-require "^0.1.0" +thread-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.1.0.tgz#d560dd8b9d09482b0e2e876a96c229c374870836" + integrity sha512-5+Pf2Ya31CsZyIPYYkhINzdTZ3guL+jHq7D8lkBybgGcSQIKDbid3NJku3SpCKeE/gACWAccDA/rH2B6doC5aA== + dependencies: + real-require "^0.2.0" + through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -3111,6 +3231,11 @@ timestring@^6.0.0: resolved "https://registry.yarnpkg.com/timestring/-/timestring-6.0.0.tgz#b0c7c331981ecf2066ce88bcfb8ee3ae32e7a0f6" integrity sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA== +tiny-lru@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-8.0.2.tgz#812fccbe6e622ded552e3ff8a4c3b5ff34a85e4c" + integrity sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -3174,14 +3299,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -3197,11 +3314,6 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -3209,21 +3321,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-value-parser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/url-value-parser/-/url-value-parser-2.1.0.tgz#fe1ae776122b2eea4bbf284896bbdcd7fc75e1fa" - integrity sha512-gIYPWXujdUdwd/9TGCHTf5Vvgw6lOxjE5Q/k+7WNByYyS0vW5WX0k+xuVlhvPq6gRNhzXVv/ezC+OfeAet5Kcw== - util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - uuid-parse@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/uuid-parse/-/uuid-parse-1.1.0.tgz#7061c5a1384ae0e1f943c538094597e1b5f3a65b" @@ -3239,11 +3341,6 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"