aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--config/default.yaml16
-rw-r--r--config/production.yaml.example16
-rw-r--r--package.json16
-rw-r--r--server.ts15
-rw-r--r--server/helpers/logger.ts101
-rw-r--r--server/initializers/config.ts16
-rw-r--r--server/initializers/constants.ts3
-rw-r--r--server/lib/activitypub/activity.ts20
-rw-r--r--server/lib/job-queue/job-queue.ts6
-rw-r--r--server/lib/opentelemetry/metric-helpers/index.ts1
-rw-r--r--server/lib/opentelemetry/metric-helpers/stats-observers-builder.ts186
-rw-r--r--server/lib/opentelemetry/metrics.ts111
-rw-r--r--server/lib/opentelemetry/tracing.ts81
-rw-r--r--server/models/video/video.ts24
-rw-r--r--server/tests/api/server/index.ts1
-rw-r--r--server/tests/api/server/no-client.ts3
-rw-r--r--server/tests/api/server/open-telemetry.ts95
-rw-r--r--server/tests/shared/checks.ts9
-rw-r--r--server/tests/shared/mock-servers/index.ts1
-rw-r--r--server/tests/shared/mock-servers/mock-http.ts23
-rw-r--r--server/types/express.d.ts2
-rw-r--r--shared/models/server/server-stats.model.ts33
-rw-r--r--yarn.lock313
23 files changed, 1002 insertions, 90 deletions
diff --git a/config/default.yaml b/config/default.yaml
index 8e5d13355..8458ee016 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -177,6 +177,22 @@ log:
177 log_tracker_unknown_infohash: true 177 log_tracker_unknown_infohash: true
178 prettify_sql: false 178 prettify_sql: false
179 179
180# Highly experimental support of Open Telemetry
181open_telemetry:
182 metrics:
183 enabled: false
184
185 # Create a prometheus exporter server on this port so prometheus server can scrape PeerTube metrics
186 prometheus_exporter:
187 port: 9091
188
189 tracing:
190 enabled: false
191
192 # Send traces to a Jaeger compatible endpoint
193 jaeger_exporter:
194 endpoint: ''
195
180trending: 196trending:
181 videos: 197 videos:
182 interval_days: 7 # Compute trending videos for the last x days 198 interval_days: 7 # Compute trending videos for the last x days
diff --git a/config/production.yaml.example b/config/production.yaml.example
index ef0358221..3da2421fe 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -175,6 +175,22 @@ log:
175 log_tracker_unknown_infohash: true 175 log_tracker_unknown_infohash: true
176 prettify_sql: false 176 prettify_sql: false
177 177
178# Highly experimental support of Open Telemetry
179open_telemetry:
180 metrics:
181 enabled: false
182
183 # Create a prometheus exporter server on this port so prometheus server can scrape PeerTube metrics
184 prometheus_exporter:
185 port: 9091
186
187 tracing:
188 enabled: false
189
190 # Send traces to a Jaeger compatible endpoint
191 jaeger_exporter:
192 endpoint: ''
193
178trending: 194trending:
179 videos: 195 videos:
180 interval_days: 7 # Compute trending videos for the last x days 196 interval_days: 7 # Compute trending videos for the last x days
diff --git a/package.json b/package.json
index 6a5bcf75c..79bc7cf1f 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,22 @@
85 "@aws-sdk/node-http-handler": "^3.82.0", 85 "@aws-sdk/node-http-handler": "^3.82.0",
86 "@babel/parser": "7.17.8", 86 "@babel/parser": "7.17.8",
87 "@node-oauth/oauth2-server": "^4.2.0", 87 "@node-oauth/oauth2-server": "^4.2.0",
88 "@opentelemetry/api": "^1.1.0",
89 "@opentelemetry/api-metrics": "^0.29.2",
90 "@opentelemetry/exporter-jaeger": "^1.3.1",
91 "@opentelemetry/exporter-prometheus": "~0.29.2",
92 "@opentelemetry/instrumentation": "^0.29.2",
93 "@opentelemetry/instrumentation-dns": "^0.29.0",
94 "@opentelemetry/instrumentation-express": "^0.30.0",
95 "@opentelemetry/instrumentation-fs": "^0.4.0",
96 "@opentelemetry/instrumentation-http": "^0.29.2",
97 "@opentelemetry/instrumentation-pg": "^0.30.0",
98 "@opentelemetry/instrumentation-redis-4": "^0.31.0",
99 "@opentelemetry/resources": "^1.3.1",
100 "@opentelemetry/sdk-metrics-base": "~0.29.2",
101 "@opentelemetry/sdk-trace-base": "^1.3.1",
102 "@opentelemetry/sdk-trace-node": "^1.3.1",
103 "@opentelemetry/semantic-conventions": "^1.3.1",
88 "@peertube/feed": "^5.0.1", 104 "@peertube/feed": "^5.0.1",
89 "@peertube/http-signature": "^1.6.0", 105 "@peertube/http-signature": "^1.6.0",
90 "@uploadx/core": "^5.1.2", 106 "@uploadx/core": "^5.1.2",
diff --git a/server.ts b/server.ts
index 559327f16..73b7441f9 100644
--- a/server.ts
+++ b/server.ts
@@ -1,4 +1,7 @@
1// ----------- Node modules ----------- 1// ----------- Node modules -----------
2import { registerOpentelemetryTracing } from './server/lib/opentelemetry/tracing'
3registerOpentelemetryTracing()
4
2import express from 'express' 5import express from 'express'
3import morgan, { token } from 'morgan' 6import morgan, { token } from 'morgan'
4import cors from 'cors' 7import cors from 'cors'
@@ -47,6 +50,12 @@ checkConfig()
47// Trust our proxy (IP forwarding...) 50// Trust our proxy (IP forwarding...)
48app.set('trust proxy', CONFIG.TRUST_PROXY) 51app.set('trust proxy', CONFIG.TRUST_PROXY)
49 52
53app.use((_req, res, next) => {
54 res.locals.requestStart = Date.now()
55
56 return next()
57})
58
50// Security middleware 59// Security middleware
51import { baseCSP } from './server/middlewares/csp' 60import { baseCSP } from './server/middlewares/csp'
52 61
@@ -126,6 +135,7 @@ import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache
126import { ServerConfigManager } from '@server/lib/server-config-manager' 135import { ServerConfigManager } from '@server/lib/server-config-manager'
127import { VideoViewsManager } from '@server/lib/views/video-views-manager' 136import { VideoViewsManager } from '@server/lib/views/video-views-manager'
128import { isTestInstance } from './server/helpers/core-utils' 137import { isTestInstance } from './server/helpers/core-utils'
138import { OpenTelemetryMetrics } from '@server/lib/opentelemetry/metrics'
129 139
130// ----------- Command line ----------- 140// ----------- Command line -----------
131 141
@@ -194,6 +204,10 @@ app.use(cookieParser())
194// W3C DNT Tracking Status 204// W3C DNT Tracking Status
195app.use(advertiseDoNotTrack) 205app.use(advertiseDoNotTrack)
196 206
207// ----------- Open Telemetry -----------
208
209OpenTelemetryMetrics.Instance.init(app)
210
197// ----------- Views, routes and static files ----------- 211// ----------- Views, routes and static files -----------
198 212
199// API 213// API
@@ -297,6 +311,7 @@ async function startApplication () {
297 RemoveDanglingResumableUploadsScheduler.Instance.enable() 311 RemoveDanglingResumableUploadsScheduler.Instance.enable()
298 VideoViewsBufferScheduler.Instance.enable() 312 VideoViewsBufferScheduler.Instance.enable()
299 GeoIPUpdateScheduler.Instance.enable() 313 GeoIPUpdateScheduler.Instance.enable()
314 OpenTelemetryMetrics.Instance.registerMetrics()
300 315
301 Redis.Instance.init() 316 Redis.Instance.init()
302 PeerTubeSocket.Instance.init(server) 317 PeerTubeSocket.Instance.init(server)
diff --git a/server/helpers/logger.ts b/server/helpers/logger.ts
index 4fbaf8a73..9625c1b33 100644
--- a/server/helpers/logger.ts
+++ b/server/helpers/logger.ts
@@ -1,54 +1,18 @@
1// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
2import { stat } from 'fs-extra' 1import { stat } from 'fs-extra'
3import { omit } from 'lodash' 2import { omit } from 'lodash'
4import { join } from 'path' 3import { join } from 'path'
5import { format as sqlFormat } from 'sql-formatter' 4import { format as sqlFormat } from 'sql-formatter'
6import { createLogger, format, transports } from 'winston' 5import { createLogger, format, transports } from 'winston'
7import { FileTransportOptions } from 'winston/lib/winston/transports' 6import { FileTransportOptions } from 'winston/lib/winston/transports'
7import { context } from '@opentelemetry/api'
8import { getSpanContext } from '@opentelemetry/api/build/src/trace/context-utils'
8import { CONFIG } from '../initializers/config' 9import { CONFIG } from '../initializers/config'
9import { LOG_FILENAME } from '../initializers/constants' 10import { LOG_FILENAME } from '../initializers/constants'
10 11
11const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT 12const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
12 13
13function getLoggerReplacer () {
14 const seen = new WeakSet()
15
16 // Thanks: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#Examples
17 return (key: string, value: any) => {
18 if (key === 'cert') return 'Replaced by the logger to avoid large log message'
19
20 if (typeof value === 'object' && value !== null) {
21 if (seen.has(value)) return
22
23 seen.add(value)
24 }
25
26 if (value instanceof Set) {
27 return Array.from(value)
28 }
29
30 if (value instanceof Map) {
31 return Array.from(value.entries())
32 }
33
34 if (value instanceof Error) {
35 const error = {}
36
37 Object.getOwnPropertyNames(value).forEach(key => { error[key] = value[key] })
38
39 return error
40 }
41
42 return value
43 }
44}
45
46const consoleLoggerFormat = format.printf(info => { 14const consoleLoggerFormat = format.printf(info => {
47 const toOmit = [ 'label', 'timestamp', 'level', 'message', 'sql', 'tags' ] 15 let additionalInfos = JSON.stringify(getAdditionalInfo(info), removeCyclicValues(), 2)
48
49 const obj = omit(info, ...toOmit)
50
51 let additionalInfos = JSON.stringify(obj, getLoggerReplacer(), 2)
52 16
53 if (additionalInfos === undefined || additionalInfos === '{}') additionalInfos = '' 17 if (additionalInfos === undefined || additionalInfos === '{}') additionalInfos = ''
54 else additionalInfos = ' ' + additionalInfos 18 else additionalInfos = ' ' + additionalInfos
@@ -68,7 +32,7 @@ const consoleLoggerFormat = format.printf(info => {
68}) 32})
69 33
70const jsonLoggerFormat = format.printf(info => { 34const jsonLoggerFormat = format.printf(info => {
71 return JSON.stringify(info, getLoggerReplacer()) 35 return JSON.stringify(info, removeCyclicValues())
72}) 36})
73 37
74const timestampFormatter = format.timestamp({ 38const timestampFormatter = format.timestamp({
@@ -94,11 +58,14 @@ if (CONFIG.LOG.ROTATION.ENABLED) {
94 fileLoggerOptions.maxFiles = CONFIG.LOG.ROTATION.MAX_FILES 58 fileLoggerOptions.maxFiles = CONFIG.LOG.ROTATION.MAX_FILES
95} 59}
96 60
97const logger = buildLogger()
98
99function buildLogger (labelSuffix?: string) { 61function buildLogger (labelSuffix?: string) {
100 return createLogger({ 62 return createLogger({
101 level: CONFIG.LOG.LEVEL, 63 level: CONFIG.LOG.LEVEL,
64 defaultMeta: {
65 get traceId () { return getSpanContext(context.active())?.traceId },
66 get spanId () { return getSpanContext(context.active())?.spanId },
67 get traceFlags () { return getSpanContext(context.active())?.traceFlags }
68 },
102 format: format.combine( 69 format: format.combine(
103 labelFormatter(labelSuffix), 70 labelFormatter(labelSuffix),
104 format.splat() 71 format.splat()
@@ -118,6 +85,10 @@ function buildLogger (labelSuffix?: string) {
118 }) 85 })
119} 86}
120 87
88const logger = buildLogger()
89
90// ---------------------------------------------------------------------------
91
121function bunyanLogFactory (level: string) { 92function bunyanLogFactory (level: string) {
122 return function (...params: any[]) { 93 return function (...params: any[]) {
123 let meta = null 94 let meta = null
@@ -141,12 +112,15 @@ const bunyanLogger = {
141 level: () => { }, 112 level: () => { },
142 trace: bunyanLogFactory('debug'), 113 trace: bunyanLogFactory('debug'),
143 debug: bunyanLogFactory('debug'), 114 debug: bunyanLogFactory('debug'),
115 verbose: bunyanLogFactory('debug'),
144 info: bunyanLogFactory('info'), 116 info: bunyanLogFactory('info'),
145 warn: bunyanLogFactory('warn'), 117 warn: bunyanLogFactory('warn'),
146 error: bunyanLogFactory('error'), 118 error: bunyanLogFactory('error'),
147 fatal: bunyanLogFactory('error') 119 fatal: bunyanLogFactory('error')
148} 120}
149 121
122// ---------------------------------------------------------------------------
123
150type LoggerTagsFn = (...tags: string[]) => { tags: string[] } 124type LoggerTagsFn = (...tags: string[]) => { tags: string[] }
151function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn { 125function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn {
152 return (...tags: string[]) => { 126 return (...tags: string[]) => {
@@ -154,6 +128,8 @@ function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn {
154 } 128 }
155} 129}
156 130
131// ---------------------------------------------------------------------------
132
157async function mtimeSortFilesDesc (files: string[], basePath: string) { 133async function mtimeSortFilesDesc (files: string[], basePath: string) {
158 const promises = [] 134 const promises = []
159 const out: { file: string, mtime: number }[] = [] 135 const out: { file: string, mtime: number }[] = []
@@ -189,3 +165,44 @@ export {
189 loggerTagsFactory, 165 loggerTagsFactory,
190 bunyanLogger 166 bunyanLogger
191} 167}
168
169// ---------------------------------------------------------------------------
170
171function removeCyclicValues () {
172 const seen = new WeakSet()
173
174 // Thanks: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#Examples
175 return (key: string, value: any) => {
176 if (key === 'cert') return 'Replaced by the logger to avoid large log message'
177
178 if (typeof value === 'object' && value !== null) {
179 if (seen.has(value)) return
180
181 seen.add(value)
182 }
183
184 if (value instanceof Set) {
185 return Array.from(value)
186 }
187
188 if (value instanceof Map) {
189 return Array.from(value.entries())
190 }
191
192 if (value instanceof Error) {
193 const error = {}
194
195 Object.getOwnPropertyNames(value).forEach(key => { error[key] = value[key] })
196
197 return error
198 }
199
200 return value
201 }
202}
203
204function getAdditionalInfo (info: any) {
205 const toOmit = [ 'label', 'timestamp', 'level', 'message', 'sql', 'tags' ]
206
207 return omit(info, ...toOmit)
208}
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index 754585981..0943ffe2d 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -167,6 +167,22 @@ const CONFIG = {
167 LOG_TRACKER_UNKNOWN_INFOHASH: config.get<boolean>('log.log_tracker_unknown_infohash'), 167 LOG_TRACKER_UNKNOWN_INFOHASH: config.get<boolean>('log.log_tracker_unknown_infohash'),
168 PRETTIFY_SQL: config.get<boolean>('log.prettify_sql') 168 PRETTIFY_SQL: config.get<boolean>('log.prettify_sql')
169 }, 169 },
170 OPEN_TELEMETRY: {
171 METRICS: {
172 ENABLED: config.get<boolean>('open_telemetry.metrics.enabled'),
173
174 PROMETHEUS_EXPORTER: {
175 PORT: config.get<number>('open_telemetry.metrics.prometheus_exporter.port')
176 }
177 },
178 TRACING: {
179 ENABLED: config.get<boolean>('open_telemetry.tracing.enabled'),
180
181 JAEGER_EXPORTER: {
182 ENDPOINT: config.get<string>('open_telemetry.tracing.jaeger_exporter.endpoint')
183 }
184 }
185 },
170 TRENDING: { 186 TRENDING: {
171 VIDEOS: { 187 VIDEOS: {
172 INTERVAL_DAYS: config.get<number>('trending.videos.interval_days'), 188 INTERVAL_DAYS: config.get<number>('trending.videos.interval_days'),
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index c6989c38b..e3683269c 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -736,7 +736,8 @@ const MEMOIZE_TTL = {
736 INFO_HASH_EXISTS: 1000 * 3600 * 12, // 12 hours 736 INFO_HASH_EXISTS: 1000 * 3600 * 12, // 12 hours
737 VIDEO_DURATION: 1000 * 10, // 10 seconds 737 VIDEO_DURATION: 1000 * 10, // 10 seconds
738 LIVE_ABLE_TO_UPLOAD: 1000 * 60, // 1 minute 738 LIVE_ABLE_TO_UPLOAD: 1000 * 60, // 1 minute
739 LIVE_CHECK_SOCKET_HEALTH: 1000 * 60 // 1 minute 739 LIVE_CHECK_SOCKET_HEALTH: 1000 * 60, // 1 minute
740 GET_STATS_FOR_OPEN_TELEMETRY_METRICS: 1000 * 60 // 1 minute
740} 741}
741 742
742const MEMOIZE_LENGTH = { 743const MEMOIZE_LENGTH = {
diff --git a/server/lib/activitypub/activity.ts b/server/lib/activitypub/activity.ts
index e6cec1ba7..ba2967ce9 100644
--- a/server/lib/activitypub/activity.ts
+++ b/server/lib/activitypub/activity.ts
@@ -1,3 +1,5 @@
1import { ActivityType } from "@shared/models"
2
1function getAPId (object: string | { id: string }) { 3function getAPId (object: string | { id: string }) {
2 if (typeof object === 'string') return object 4 if (typeof object === 'string') return object
3 5
@@ -13,8 +15,26 @@ function getDurationFromActivityStream (duration: string) {
13 return parseInt(duration.replace(/[^\d]+/, '')) 15 return parseInt(duration.replace(/[^\d]+/, ''))
14} 16}
15 17
18function buildAvailableActivities (): ActivityType[] {
19 return [
20 'Create',
21 'Update',
22 'Delete',
23 'Follow',
24 'Accept',
25 'Announce',
26 'Undo',
27 'Like',
28 'Reject',
29 'View',
30 'Dislike',
31 'Flag'
32 ]
33}
34
16export { 35export {
17 getAPId, 36 getAPId,
18 getActivityStreamDuration, 37 getActivityStreamDuration,
38 buildAvailableActivities,
19 getDurationFromActivityStream 39 getDurationFromActivityStream
20} 40}
diff --git a/server/lib/job-queue/job-queue.ts b/server/lib/job-queue/job-queue.ts
index ce24763f1..e55d2e7c2 100644
--- a/server/lib/job-queue/job-queue.ts
+++ b/server/lib/job-queue/job-queue.ts
@@ -285,6 +285,12 @@ class JobQueue {
285 return total 285 return total
286 } 286 }
287 287
288 async getStats () {
289 const promises = jobTypes.map(async t => ({ jobType: t, counts: await this.queues[t].getJobCounts() }))
290
291 return Promise.all(promises)
292 }
293
288 async removeOldJobs () { 294 async removeOldJobs () {
289 for (const key of Object.keys(this.queues)) { 295 for (const key of Object.keys(this.queues)) {
290 const queue = this.queues[key] 296 const queue = this.queues[key]
diff --git a/server/lib/opentelemetry/metric-helpers/index.ts b/server/lib/opentelemetry/metric-helpers/index.ts
new file mode 100644
index 000000000..cabb27326
--- /dev/null
+++ b/server/lib/opentelemetry/metric-helpers/index.ts
@@ -0,0 +1 @@
export * from './stats-observers-builder'
diff --git a/server/lib/opentelemetry/metric-helpers/stats-observers-builder.ts b/server/lib/opentelemetry/metric-helpers/stats-observers-builder.ts
new file mode 100644
index 000000000..90b58f33d
--- /dev/null
+++ b/server/lib/opentelemetry/metric-helpers/stats-observers-builder.ts
@@ -0,0 +1,186 @@
1import memoizee from 'memoizee'
2import { Meter } from '@opentelemetry/api-metrics'
3import { MEMOIZE_TTL } from '@server/initializers/constants'
4import { buildAvailableActivities } from '@server/lib/activitypub/activity'
5import { StatsManager } from '@server/lib/stat-manager'
6
7export class StatsObserverBuilder {
8
9 private readonly getInstanceStats = memoizee(() => {
10 return StatsManager.Instance.getStats()
11 }, { maxAge: MEMOIZE_TTL.GET_STATS_FOR_OPEN_TELEMETRY_METRICS })
12
13 constructor (private readonly meter: Meter) {
14
15 }
16
17 buildObservers () {
18 this.buildUserStatsObserver()
19 this.buildVideoStatsObserver()
20 this.buildCommentStatsObserver()
21 this.buildPlaylistStatsObserver()
22 this.buildChannelStatsObserver()
23 this.buildInstanceFollowsStatsObserver()
24 this.buildRedundancyStatsObserver()
25 this.buildActivityPubStatsObserver()
26 }
27
28 private buildUserStatsObserver () {
29 this.meter.createObservableGauge('peertube_users_total', {
30 description: 'Total users on the instance'
31 }).addCallback(async observableResult => {
32 const stats = await this.getInstanceStats()
33
34 observableResult.observe(stats.totalUsers)
35 })
36
37 this.meter.createObservableGauge('peertube_active_users_total', {
38 description: 'Total active users on the instance'
39 }).addCallback(async observableResult => {
40 const stats = await this.getInstanceStats()
41
42 observableResult.observe(stats.totalDailyActiveUsers, { activeInterval: 'daily' })
43 observableResult.observe(stats.totalWeeklyActiveUsers, { activeInterval: 'weekly' })
44 observableResult.observe(stats.totalMonthlyActiveUsers, { activeInterval: 'monthly' })
45 })
46 }
47
48 private buildChannelStatsObserver () {
49 this.meter.createObservableGauge('peertube_channels_total', {
50 description: 'Total channels on the instance'
51 }).addCallback(async observableResult => {
52 const stats = await this.getInstanceStats()
53
54 observableResult.observe(stats.totalLocalVideoChannels, { channelOrigin: 'local' })
55 })
56
57 this.meter.createObservableGauge('peertube_active_channels_total', {
58 description: 'Total active channels on the instance'
59 }).addCallback(async observableResult => {
60 const stats = await this.getInstanceStats()
61
62 observableResult.observe(stats.totalLocalDailyActiveVideoChannels, { channelOrigin: 'local', activeInterval: 'daily' })
63 observableResult.observe(stats.totalLocalWeeklyActiveVideoChannels, { channelOrigin: 'local', activeInterval: 'weekly' })
64 observableResult.observe(stats.totalLocalMonthlyActiveVideoChannels, { channelOrigin: 'local', activeInterval: 'monthly' })
65 })
66 }
67
68 private buildVideoStatsObserver () {
69 this.meter.createObservableGauge('peertube_videos_total', {
70 description: 'Total videos on the instance'
71 }).addCallback(async observableResult => {
72 const stats = await this.getInstanceStats()
73
74 observableResult.observe(stats.totalLocalVideos, { videoOrigin: 'local' })
75 observableResult.observe(stats.totalVideos - stats.totalLocalVideos, { videoOrigin: 'remote' })
76 })
77
78 this.meter.createObservableGauge('peertube_video_views_total', {
79 description: 'Total video views made on the instance'
80 }).addCallback(async observableResult => {
81 const stats = await this.getInstanceStats()
82
83 observableResult.observe(stats.totalLocalVideoViews, { viewOrigin: 'local' })
84 })
85
86 this.meter.createObservableGauge('peertube_video_bytes_total', {
87 description: 'Total bytes of videos'
88 }).addCallback(async observableResult => {
89 const stats = await this.getInstanceStats()
90
91 observableResult.observe(stats.totalLocalVideoFilesSize, { videoOrigin: 'local' })
92 })
93 }
94
95 private buildCommentStatsObserver () {
96 this.meter.createObservableGauge('peertube_comments_total', {
97 description: 'Total comments on the instance'
98 }).addCallback(async observableResult => {
99 const stats = await this.getInstanceStats()
100
101 observableResult.observe(stats.totalLocalVideoComments, { accountOrigin: 'local' })
102 })
103 }
104
105 private buildPlaylistStatsObserver () {
106 this.meter.createObservableGauge('peertube_playlists_total', {
107 description: 'Total playlists on the instance'
108 }).addCallback(async observableResult => {
109 const stats = await this.getInstanceStats()
110
111 observableResult.observe(stats.totalLocalPlaylists, { playlistOrigin: 'local' })
112 })
113 }
114
115 private buildInstanceFollowsStatsObserver () {
116 this.meter.createObservableGauge('peertube_instance_followers_total', {
117 description: 'Total followers of the instance'
118 }).addCallback(async observableResult => {
119 const stats = await this.getInstanceStats()
120
121 observableResult.observe(stats.totalInstanceFollowers)
122 })
123
124 this.meter.createObservableGauge('peertube_instance_following_total', {
125 description: 'Total following of the instance'
126 }).addCallback(async observableResult => {
127 const stats = await this.getInstanceStats()
128
129 observableResult.observe(stats.totalInstanceFollowing)
130 })
131 }
132
133 private buildRedundancyStatsObserver () {
134 this.meter.createObservableGauge('peertube_redundancy_used_bytes_total', {
135 description: 'Total redundancy used of the instance'
136 }).addCallback(async observableResult => {
137 const stats = await this.getInstanceStats()
138
139 for (const r of stats.videosRedundancy) {
140 observableResult.observe(r.totalUsed, { strategy: r.strategy })
141 }
142 })
143
144 this.meter.createObservableGauge('peertube_redundancy_available_bytes_total', {
145 description: 'Total redundancy available of the instance'
146 }).addCallback(async observableResult => {
147 const stats = await this.getInstanceStats()
148
149 for (const r of stats.videosRedundancy) {
150 observableResult.observe(r.totalSize, { strategy: r.strategy })
151 }
152 })
153 }
154
155 private buildActivityPubStatsObserver () {
156 const availableActivities = buildAvailableActivities()
157
158 this.meter.createObservableGauge('peertube_ap_inbox_success_total', {
159 description: 'Total inbox messages processed with success'
160 }).addCallback(async observableResult => {
161 const stats = await this.getInstanceStats()
162
163 for (const type of availableActivities) {
164 observableResult.observe(stats[`totalActivityPub${type}MessagesSuccesses`], { activityType: type })
165 }
166 })
167
168 this.meter.createObservableGauge('peertube_ap_inbox_error_total', {
169 description: 'Total inbox messages processed with error'
170 }).addCallback(async observableResult => {
171 const stats = await this.getInstanceStats()
172
173 for (const type of availableActivities) {
174 observableResult.observe(stats[`totalActivityPub${type}MessagesErrors`], { activityType: type })
175 }
176 })
177
178 this.meter.createObservableGauge('peertube_ap_inbox_waiting_total', {
179 description: 'Total inbox messages waiting for being processed'
180 }).addCallback(async observableResult => {
181 const stats = await this.getInstanceStats()
182
183 observableResult.observe(stats.totalActivityPubMessagesWaiting)
184 })
185 }
186}
diff --git a/server/lib/opentelemetry/metrics.ts b/server/lib/opentelemetry/metrics.ts
new file mode 100644
index 000000000..ca0aae8e7
--- /dev/null
+++ b/server/lib/opentelemetry/metrics.ts
@@ -0,0 +1,111 @@
1import { Application, Request, Response } from 'express'
2import { Meter, metrics } from '@opentelemetry/api-metrics'
3import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'
4import { MeterProvider } from '@opentelemetry/sdk-metrics-base'
5import { logger } from '@server/helpers/logger'
6import { CONFIG } from '@server/initializers/config'
7import { JobQueue } from '../job-queue'
8import { StatsObserverBuilder } from './metric-helpers'
9
10class OpenTelemetryMetrics {
11
12 private static instance: OpenTelemetryMetrics
13
14 private meter: Meter
15
16 private onRequestDuration: (req: Request, res: Response) => void
17
18 private constructor () {}
19
20 init (app: Application) {
21 if (CONFIG.OPEN_TELEMETRY.METRICS.ENABLED !== true) return
22
23 app.use((req, res, next) => {
24 res.once('finish', () => {
25 if (!this.onRequestDuration) return
26
27 this.onRequestDuration(req as Request, res as Response)
28 })
29
30 next()
31 })
32 }
33
34 registerMetrics () {
35 if (CONFIG.OPEN_TELEMETRY.METRICS.ENABLED !== true) return
36
37 logger.info('Registering Open Telemetry metrics')
38
39 const provider = new MeterProvider()
40
41 provider.addMetricReader(new PrometheusExporter({ port: CONFIG.OPEN_TELEMETRY.METRICS.PROMETHEUS_EXPORTER.PORT }))
42
43 metrics.setGlobalMeterProvider(provider)
44
45 this.meter = metrics.getMeter('default')
46
47 this.buildMemoryObserver()
48 this.buildRequestObserver()
49 this.buildJobQueueObserver()
50
51 const statsObserverBuilder = new StatsObserverBuilder(this.meter)
52 statsObserverBuilder.buildObservers()
53 }
54
55 private buildMemoryObserver () {
56 this.meter.createObservableGauge('nodejs_memory_usage_bytes', {
57 description: 'Memory'
58 }).addCallback(observableResult => {
59 const current = process.memoryUsage()
60
61 observableResult.observe(current.heapTotal, { memoryType: 'heapTotal' })
62 observableResult.observe(current.heapUsed, { memoryType: 'heapUsed' })
63 observableResult.observe(current.arrayBuffers, { memoryType: 'arrayBuffers' })
64 observableResult.observe(current.external, { memoryType: 'external' })
65 observableResult.observe(current.rss, { memoryType: 'rss' })
66 })
67 }
68
69 private buildJobQueueObserver () {
70 this.meter.createObservableGauge('peertube_job_queue_total', {
71 description: 'Total jobs in the PeerTube job queue'
72 }).addCallback(async observableResult => {
73 const stats = await JobQueue.Instance.getStats()
74
75 for (const { jobType, counts } of stats) {
76 for (const state of Object.keys(counts)) {
77 observableResult.observe(counts[state], { jobType, state })
78 }
79 }
80 })
81 }
82
83 private buildRequestObserver () {
84 const requestDuration = this.meter.createHistogram('http_request_duration_ms', {
85 unit: 'milliseconds',
86 description: 'Duration of HTTP requests in ms'
87 })
88
89 this.onRequestDuration = (req: Request, res: Response) => {
90 const duration = Date.now() - res.locals.requestStart
91
92 requestDuration.record(duration, {
93 path: this.buildRequestPath(req.originalUrl),
94 method: req.method,
95 statusCode: res.statusCode + ''
96 })
97 }
98 }
99
100 private buildRequestPath (path: string) {
101 return path.split('?')[0]
102 }
103
104 static get Instance () {
105 return this.instance || (this.instance = new this())
106 }
107}
108
109export {
110 OpenTelemetryMetrics
111}
diff --git a/server/lib/opentelemetry/tracing.ts b/server/lib/opentelemetry/tracing.ts
new file mode 100644
index 000000000..5358d04de
--- /dev/null
+++ b/server/lib/opentelemetry/tracing.ts
@@ -0,0 +1,81 @@
1import { diag, DiagLogLevel, trace } from '@opentelemetry/api'
2import { JaegerExporter } from '@opentelemetry/exporter-jaeger'
3import { registerInstrumentations } from '@opentelemetry/instrumentation'
4import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns'
5import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'
6import FsInstrumentation from '@opentelemetry/instrumentation-fs'
7import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'
8import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'
9import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis-4'
10import { Resource } from '@opentelemetry/resources'
11import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
12import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'
13import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
14import { logger } from '@server/helpers/logger'
15import { CONFIG } from '@server/initializers/config'
16
17function registerOpentelemetryTracing () {
18 if (CONFIG.OPEN_TELEMETRY.TRACING.ENABLED !== true) return
19
20 logger.info('Registering Open Telemetry tracing')
21
22 const customLogger = (level: string) => {
23 return (message: string, ...args: unknown[]) => {
24 let fullMessage = message
25
26 for (const arg of args) {
27 if (typeof arg === 'string') fullMessage += arg
28 else break
29 }
30
31 logger[level](fullMessage)
32 }
33 }
34
35 diag.setLogger({
36 error: customLogger('error'),
37 warn: customLogger('warn'),
38 info: customLogger('info'),
39 debug: customLogger('debug'),
40 verbose: customLogger('verbose')
41 }, DiagLogLevel.INFO)
42
43 const tracerProvider = new NodeTracerProvider({
44 resource: new Resource({
45 [SemanticResourceAttributes.SERVICE_NAME]: 'peertube'
46 })
47 })
48
49 registerInstrumentations({
50 tracerProvider: tracerProvider,
51 instrumentations: [
52 new PgInstrumentation({
53 enhancedDatabaseReporting: true
54 }),
55 new DnsInstrumentation(),
56 new HttpInstrumentation(),
57 new ExpressInstrumentation(),
58 new RedisInstrumentation({
59 dbStatementSerializer: function (cmdName, cmdArgs) {
60 return [ cmdName, ...cmdArgs ].join(' ')
61 }
62 }),
63 new FsInstrumentation()
64 ]
65 })
66
67 tracerProvider.addSpanProcessor(
68 new BatchSpanProcessor(
69 new JaegerExporter({ endpoint: CONFIG.OPEN_TELEMETRY.TRACING.JAEGER_EXPORTER.ENDPOINT })
70 )
71 )
72
73 tracerProvider.register()
74}
75
76const tracer = trace.getTracer('peertube')
77
78export {
79 registerOpentelemetryTracing,
80 tracer
81}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index e5f8b5fa2..4f711b2fa 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -24,7 +24,6 @@ import {
24 Table, 24 Table,
25 UpdatedAt 25 UpdatedAt
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { buildNSFWFilter } from '@server/helpers/express-utils'
28import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 27import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
29import { LiveManager } from '@server/lib/live/live-manager' 28import { LiveManager } from '@server/lib/live/live-manager'
30import { removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage' 29import { removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage'
@@ -134,9 +133,9 @@ import { VideoJobInfoModel } from './video-job-info'
134import { VideoLiveModel } from './video-live' 133import { VideoLiveModel } from './video-live'
135import { VideoPlaylistElementModel } from './video-playlist-element' 134import { VideoPlaylistElementModel } from './video-playlist-element'
136import { VideoShareModel } from './video-share' 135import { VideoShareModel } from './video-share'
136import { VideoSourceModel } from './video-source'
137import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 137import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
138import { VideoTagModel } from './video-tag' 138import { VideoTagModel } from './video-tag'
139import { VideoSourceModel } from './video-source'
140 139
141export enum ScopeNames { 140export enum ScopeNames {
142 FOR_API = 'FOR_API', 141 FOR_API = 'FOR_API',
@@ -1370,11 +1369,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1370 } 1369 }
1371 1370
1372 static async getStats () { 1371 static async getStats () {
1373 const totalLocalVideos = await VideoModel.count({ 1372 const serverActor = await getServerActor()
1374 where: {
1375 remote: false
1376 }
1377 })
1378 1373
1379 let totalLocalVideoViews = await VideoModel.sum('views', { 1374 let totalLocalVideoViews = await VideoModel.sum('views', {
1380 where: { 1375 where: {
@@ -1385,19 +1380,26 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1385 // Sequelize could return null... 1380 // Sequelize could return null...
1386 if (!totalLocalVideoViews) totalLocalVideoViews = 0 1381 if (!totalLocalVideoViews) totalLocalVideoViews = 0
1387 1382
1388 const serverActor = await getServerActor() 1383 const baseOptions = {
1389
1390 const { total: totalVideos } = await VideoModel.listForApi({
1391 start: 0, 1384 start: 0,
1392 count: 0, 1385 count: 0,
1393 sort: '-publishedAt', 1386 sort: '-publishedAt',
1394 nsfw: buildNSFWFilter(), 1387 nsfw: null,
1388 isLocal: true,
1395 displayOnlyForFollower: { 1389 displayOnlyForFollower: {
1396 actorId: serverActor.id, 1390 actorId: serverActor.id,
1397 orLocalVideos: true 1391 orLocalVideos: true
1398 } 1392 }
1393 }
1394
1395 const { total: totalLocalVideos } = await VideoModel.listForApi({
1396 ...baseOptions,
1397
1398 isLocal: true
1399 }) 1399 })
1400 1400
1401 const { total: totalVideos } = await VideoModel.listForApi(baseOptions)
1402
1401 return { 1403 return {
1402 totalLocalVideos, 1404 totalLocalVideos,
1403 totalLocalVideoViews, 1405 totalLocalVideoViews,
diff --git a/server/tests/api/server/index.ts b/server/tests/api/server/index.ts
index 45be107ce..78522c246 100644
--- a/server/tests/api/server/index.ts
+++ b/server/tests/api/server/index.ts
@@ -17,5 +17,6 @@ import './slow-follows'
17import './stats' 17import './stats'
18import './tracker' 18import './tracker'
19import './no-client' 19import './no-client'
20import './open-telemetry'
20import './plugins' 21import './plugins'
21import './proxy' 22import './proxy'
diff --git a/server/tests/api/server/no-client.ts b/server/tests/api/server/no-client.ts
index 913907788..193f6c987 100644
--- a/server/tests/api/server/no-client.ts
+++ b/server/tests/api/server/no-client.ts
@@ -1,7 +1,6 @@
1import 'mocha'
2import request from 'supertest' 1import request from 'supertest'
3import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/server-commands'
4import { HttpStatusCode } from '@shared/models' 2import { HttpStatusCode } from '@shared/models'
3import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/server-commands'
5 4
6describe('Start and stop server without web client routes', function () { 5describe('Start and stop server without web client routes', function () {
7 let server: PeerTubeServer 6 let server: PeerTubeServer
diff --git a/server/tests/api/server/open-telemetry.ts b/server/tests/api/server/open-telemetry.ts
new file mode 100644
index 000000000..20909429f
--- /dev/null
+++ b/server/tests/api/server/open-telemetry.ts
@@ -0,0 +1,95 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { expectLogContain, expectLogDoesNotContain, MockHTTP } from '@server/tests/shared'
5import { HttpStatusCode, VideoPrivacy } from '@shared/models'
6import { cleanupTests, createSingleServer, makeRawRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
7
8describe('Open Telemetry', function () {
9 let server: PeerTubeServer
10
11 describe('Metrics', function () {
12 const metricsUrl = 'http://localhost:9091/metrics'
13
14 it('Should not enable open telemetry metrics', async function () {
15 server = await createSingleServer(1)
16
17 let hasError = false
18 try {
19 await makeRawRequest(metricsUrl, HttpStatusCode.NOT_FOUND_404)
20 } catch (err) {
21 hasError = err.message.includes('ECONNREFUSED')
22 }
23
24 expect(hasError).to.be.true
25
26 await server.kill()
27 })
28
29 it('Should enable open telemetry metrics', async function () {
30 server = await createSingleServer(1, {
31 open_telemetry: {
32 metrics: {
33 enabled: true
34 }
35 }
36 })
37
38 const res = await makeRawRequest(metricsUrl, HttpStatusCode.OK_200)
39 expect(res.text).to.contain('peertube_job_queue_total')
40
41 await server.kill()
42 })
43 })
44
45 describe('Tracing', function () {
46 let mockHTTP: MockHTTP
47 let mockPort: number
48
49 before(async function () {
50 mockHTTP = new MockHTTP()
51 mockPort = await mockHTTP.initialize()
52 })
53
54 it('Should enable open telemetry tracing', async function () {
55 server = await createSingleServer(1)
56
57 await expectLogDoesNotContain(server, 'Registering Open Telemetry tracing')
58
59 await server.kill()
60 })
61
62 it('Should enable open telemetry metrics', async function () {
63 server = await createSingleServer(1, {
64 open_telemetry: {
65 tracing: {
66 enabled: true,
67 jaeger_exporter: {
68 endpoint: 'http://localhost:' + mockPort
69 }
70 }
71 }
72 })
73
74 await expectLogContain(server, 'Registering Open Telemetry tracing')
75 })
76
77 it('Should upload a video and correctly works', async function () {
78 await setAccessTokensToServers([ server ])
79
80 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PUBLIC })
81
82 const video = await server.videos.get({ id: uuid })
83
84 expect(video.name).to.equal('video')
85 })
86
87 after(async function () {
88 await mockHTTP.terminate()
89 })
90 })
91
92 after(async function () {
93 await cleanupTests([ server ])
94 })
95})
diff --git a/server/tests/shared/checks.ts b/server/tests/shared/checks.ts
index 33b917f31..55ebc6c3e 100644
--- a/server/tests/shared/checks.ts
+++ b/server/tests/shared/checks.ts
@@ -29,6 +29,12 @@ async function expectLogDoesNotContain (server: PeerTubeServer, str: string) {
29 expect(content.toString()).to.not.contain(str) 29 expect(content.toString()).to.not.contain(str)
30} 30}
31 31
32async function expectLogContain (server: PeerTubeServer, str: string) {
33 const content = await server.servers.getLogContent()
34
35 expect(content.toString()).to.contain(str)
36}
37
32async function testImage (url: string, imageName: string, imageHTTPPath: string, extension = '.jpg') { 38async function testImage (url: string, imageName: string, imageHTTPPath: string, extension = '.jpg') {
33 const res = await makeGetRequest({ 39 const res = await makeGetRequest({
34 url, 40 url,
@@ -99,5 +105,6 @@ export {
99 expectNotStartWith, 105 expectNotStartWith,
100 checkBadStartPagination, 106 checkBadStartPagination,
101 checkBadCountPagination, 107 checkBadCountPagination,
102 checkBadSortPagination 108 checkBadSortPagination,
109 expectLogContain
103} 110}
diff --git a/server/tests/shared/mock-servers/index.ts b/server/tests/shared/mock-servers/index.ts
index abf4a8203..1fa983116 100644
--- a/server/tests/shared/mock-servers/index.ts
+++ b/server/tests/shared/mock-servers/index.ts
@@ -1,5 +1,6 @@
1export * from './mock-429' 1export * from './mock-429'
2export * from './mock-email' 2export * from './mock-email'
3export * from './mock-http'
3export * from './mock-instances-index' 4export * from './mock-instances-index'
4export * from './mock-joinpeertube-versions' 5export * from './mock-joinpeertube-versions'
5export * from './mock-object-storage' 6export * from './mock-object-storage'
diff --git a/server/tests/shared/mock-servers/mock-http.ts b/server/tests/shared/mock-servers/mock-http.ts
new file mode 100644
index 000000000..b7a019e07
--- /dev/null
+++ b/server/tests/shared/mock-servers/mock-http.ts
@@ -0,0 +1,23 @@
1import express from 'express'
2import { Server } from 'http'
3import { getPort, randomListen, terminateServer } from './shared'
4
5export class MockHTTP {
6 private server: Server
7
8 async initialize () {
9 const app = express()
10
11 app.get('/*', (req: express.Request, res: express.Response, next: express.NextFunction) => {
12 return res.sendStatus(200)
13 })
14
15 this.server = await randomListen(app)
16
17 return getPort(this.server)
18 }
19
20 terminate () {
21 return terminateServer(this.server)
22 }
23}
diff --git a/server/types/express.d.ts b/server/types/express.d.ts
index 27e532c31..8f8c65102 100644
--- a/server/types/express.d.ts
+++ b/server/types/express.d.ts
@@ -103,6 +103,8 @@ declare module 'express' {
103 }) => void 103 }) => void
104 104
105 locals: { 105 locals: {
106 requestStart: number
107
106 apicache: { 108 apicache: {
107 content: string | Buffer 109 content: string | Buffer
108 write: Writable['write'] 110 write: Writable['write']
diff --git a/shared/models/server/server-stats.model.ts b/shared/models/server/server-stats.model.ts
index b1dcf2065..82f5a737f 100644
--- a/shared/models/server/server-stats.model.ts
+++ b/shared/models/server/server-stats.model.ts
@@ -1,5 +1,10 @@
1import { ActivityType } from '../activitypub'
1import { VideoRedundancyStrategyWithManual } from '../redundancy' 2import { VideoRedundancyStrategyWithManual } from '../redundancy'
2export interface ServerStats { 3
4type ActivityPubMessagesSuccess = Record<`totalActivityPub${ActivityType}MessagesSuccesses`, number>
5type ActivityPubMessagesErrors = Record<`totalActivityPub${ActivityType}MessagesErrors`, number>
6
7export interface ServerStats extends ActivityPubMessagesSuccess, ActivityPubMessagesErrors {
3 totalUsers: number 8 totalUsers: number
4 totalDailyActiveUsers: number 9 totalDailyActiveUsers: number
5 totalWeeklyActiveUsers: number 10 totalWeeklyActiveUsers: number
@@ -29,32 +34,6 @@ export interface ServerStats {
29 totalActivityPubMessagesSuccesses: number 34 totalActivityPubMessagesSuccesses: number
30 totalActivityPubMessagesErrors: number 35 totalActivityPubMessagesErrors: number
31 36
32 totalActivityPubCreateMessagesSuccesses: number
33 totalActivityPubUpdateMessagesSuccesses: number
34 totalActivityPubDeleteMessagesSuccesses: number
35 totalActivityPubFollowMessagesSuccesses: number
36 totalActivityPubAcceptMessagesSuccesses: number
37 totalActivityPubRejectMessagesSuccesses: number
38 totalActivityPubAnnounceMessagesSuccesses: number
39 totalActivityPubUndoMessagesSuccesses: number
40 totalActivityPubLikeMessagesSuccesses: number
41 totalActivityPubDislikeMessagesSuccesses: number
42 totalActivityPubFlagMessagesSuccesses: number
43 totalActivityPubViewMessagesSuccesses: number
44
45 totalActivityPubCreateMessagesErrors: number
46 totalActivityPubUpdateMessagesErrors: number
47 totalActivityPubDeleteMessagesErrors: number
48 totalActivityPubFollowMessagesErrors: number
49 totalActivityPubAcceptMessagesErrors: number
50 totalActivityPubRejectMessagesErrors: number
51 totalActivityPubAnnounceMessagesErrors: number
52 totalActivityPubUndoMessagesErrors: number
53 totalActivityPubLikeMessagesErrors: number
54 totalActivityPubDislikeMessagesErrors: number
55 totalActivityPubFlagMessagesErrors: number
56 totalActivityPubViewMessagesErrors: number
57
58 activityPubMessagesProcessedPerSecond: number 37 activityPubMessagesProcessedPerSecond: number
59 totalActivityPubMessagesWaiting: number 38 totalActivityPubMessagesWaiting: number
60} 39}
diff --git a/yarn.lock b/yarn.lock
index 4d9b5b4bd..530482b22 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1606,6 +1606,173 @@
1606 "@nodelib/fs.scandir" "2.1.5" 1606 "@nodelib/fs.scandir" "2.1.5"
1607 fastq "^1.6.0" 1607 fastq "^1.6.0"
1608 1608
1609"@opentelemetry/api-metrics@0.29.2", "@opentelemetry/api-metrics@^0.29.2":
1610 version "0.29.2"
1611 resolved "https://registry.yarnpkg.com/@opentelemetry/api-metrics/-/api-metrics-0.29.2.tgz#daa823e0965754222b49a6ae6133df8b39ff8fd2"
1612 integrity sha512-yRdF5beqKuEdsPNoO7ijWCQ9HcyN0Tlgicf8RS6gzGOI54d6Hj7yKquJ6+X9XV+CSRbRWJYb+lOsXyso7uyX2g==
1613 dependencies:
1614 "@opentelemetry/api" "^1.0.0"
1615
1616"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.1.0":
1617 version "1.1.0"
1618 resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a"
1619 integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ==
1620
1621"@opentelemetry/context-async-hooks@1.3.1":
1622 version "1.3.1"
1623 resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.3.1.tgz#6b5288b535178fb8e3053c00c30eb38d3fdff60b"
1624 integrity sha512-NKUY3SGiEEIOD3EpB8erpEF4K1iyXkWald1vJMaa973+EPTASNSXvzf8hZa7nhnUVxYbxtTJqbSRsZFfbZpw4g==
1625
1626"@opentelemetry/core@1.3.1", "@opentelemetry/core@^1.0.0":
1627 version "1.3.1"
1628 resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.3.1.tgz#6eef5c5efca9a4cd7daa0cd4c7ff28ca2317c8d7"
1629 integrity sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA==
1630 dependencies:
1631 "@opentelemetry/semantic-conventions" "1.3.1"
1632
1633"@opentelemetry/exporter-jaeger@^1.3.1":
1634 version "1.3.1"
1635 resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.3.1.tgz#00070e54abea51d5001acfaeb9fcfd537bf0d1d5"
1636 integrity sha512-uJ9811zn5TTdazyTNc4xmcDnKC8H63VRGp23ujGTxBOCFUnFzfI/kUGUJ8/O7Xok9Ulop7wuuBW3onL1WedfjA==
1637 dependencies:
1638 "@opentelemetry/core" "1.3.1"
1639 "@opentelemetry/sdk-trace-base" "1.3.1"
1640 "@opentelemetry/semantic-conventions" "1.3.1"
1641 jaeger-client "^3.15.0"
1642
1643"@opentelemetry/exporter-prometheus@~0.29.2":
1644 version "0.29.2"
1645 resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.29.2.tgz#70ca7fb37655ca57a580387607d5465b47e27ac3"
1646 integrity sha512-E5sRfUM4rzbvjxdpL1H6YRtjr8wY8+/2R4NjfxPEwrENLeeQk87V1E+YFLqAS7TfFLW7Zr4lmmamunwn5THvQA==
1647 dependencies:
1648 "@opentelemetry/api-metrics" "0.29.2"
1649 "@opentelemetry/core" "1.3.1"
1650 "@opentelemetry/sdk-metrics-base" "0.29.2"
1651
1652"@opentelemetry/instrumentation-dns@^0.29.0":
1653 version "0.29.0"
1654 resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.29.0.tgz#efd21eb9d8938de97e225b52f8a432d6072b4096"
1655 integrity sha512-3WTC4m6JKviaABiR3a+56WUMvrUp9WW9EYC0+LRpqm7RK/1a5bYq2Cozc2SlFYX9ZfWKMqGS39/fU24mKQ5toA==
1656 dependencies:
1657 "@opentelemetry/instrumentation" "^0.29.2"
1658 "@opentelemetry/semantic-conventions" "^1.0.0"
1659 semver "^7.3.2"
1660
1661"@opentelemetry/instrumentation-express@^0.30.0":
1662 version "0.30.0"
1663 resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.30.0.tgz#4f5f45ce47c8f1ac75741284d3e59c861ced4269"
1664 integrity sha512-OsCfM+ThAXh3wzsyHgXyA5HUoLMdLd6Asix2Jx8yxniruU/Gq8y4Cz7aLy/vXNckXHWO3fwwL5gb7K3dykTnAQ==
1665 dependencies:
1666 "@opentelemetry/core" "^1.0.0"
1667 "@opentelemetry/instrumentation" "^0.29.2"
1668 "@opentelemetry/semantic-conventions" "^1.0.0"
1669 "@types/express" "4.17.13"
1670
1671"@opentelemetry/instrumentation-fs@^0.4.0":
1672 version "0.4.0"
1673 resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.4.0.tgz#8d689f741c8cb706fd28306f5f70dab756d2f02c"
1674 integrity sha512-AINjLsYifpBC5R0YrkS0aaWBcql5WYr+UGste6HLJlHiselA22hBE3zP2WX4y+24eIlvDFKybwlodiNrITV16Q==
1675 dependencies:
1676 "@opentelemetry/core" "^1.0.0"
1677 "@opentelemetry/instrumentation" "^0.29.2"
1678 "@opentelemetry/semantic-conventions" "^1.0.0"
1679
1680"@opentelemetry/instrumentation-http@^0.29.2":
1681 version "0.29.2"
1682 resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.29.2.tgz#c4750c33929d476c2a656f457c83d2144c5dd844"
1683 integrity sha512-XIF9WCH03rp3vQjwXXVdTxlsXT2AG6LYfFKO8r2QC+w4F4KFuZa4J3VPYJ0L/a/6dWt34DA67eBh3l6Z1rMZrg==
1684 dependencies:
1685 "@opentelemetry/core" "1.3.1"
1686 "@opentelemetry/instrumentation" "0.29.2"
1687 "@opentelemetry/semantic-conventions" "1.3.1"
1688 semver "^7.3.5"
1689
1690"@opentelemetry/instrumentation-pg@^0.30.0":
1691 version "0.30.0"
1692 resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.30.0.tgz#5941dd2817a846e7b5bc283112a9cf46728d1f44"
1693 integrity sha512-RQ3cTTJnCBE/9GagjSpaM+yzxN25MvEwOxDFes3y8c1cqrMgqxukQLm3MbcqCQ8e1g/8d18+oyiEeBUjZJ5jnw==
1694 dependencies:
1695 "@opentelemetry/instrumentation" "^0.29.2"
1696 "@opentelemetry/semantic-conventions" "^1.0.0"
1697 "@types/pg" "8.6.1"
1698 "@types/pg-pool" "2.0.3"
1699
1700"@opentelemetry/instrumentation-redis-4@^0.31.0":
1701 version "0.31.0"
1702 resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.31.0.tgz#3db1cfc177857fc208b73565df2d54065c69a67e"
1703 integrity sha512-3DY6bkqKnVlPc2WWHelb6DnU78ryYLQFqv0lqnVsoSkr7b6hnmw1Bzuwo/5YmS4C3XuTAD4/6dZVrQJ23g8HNA==
1704 dependencies:
1705 "@opentelemetry/instrumentation" "^0.29.2"
1706 "@opentelemetry/semantic-conventions" "^1.0.0"
1707
1708"@opentelemetry/instrumentation@0.29.2", "@opentelemetry/instrumentation@^0.29.2":
1709 version "0.29.2"
1710 resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.29.2.tgz#70e6d4e1a84508f5e9d8c7c426adcd7b0dba6c95"
1711 integrity sha512-LXx5V0ONNATQFCE8C5uqnxWSm4rcXLssdLHdXjtGdxRmURqj/JO8jYefqXCD0LzsqEQ6yxOx2GZ0dgXvhBVdTw==
1712 dependencies:
1713 "@opentelemetry/api-metrics" "0.29.2"
1714 require-in-the-middle "^5.0.3"
1715 semver "^7.3.2"
1716 shimmer "^1.2.1"
1717
1718"@opentelemetry/propagator-b3@1.3.1":
1719 version "1.3.1"
1720 resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-b3/-/propagator-b3-1.3.1.tgz#39208de42afef5635e74f4bedca5961d6ce25004"
1721 integrity sha512-tEAtHsRr6l3glsmKaJiJ/7HDw/isPv+f8OBsWJqkSlfLicKes8T/1D7nEDC6jPACiEbD3f6oK1KQSpMijC9/UQ==
1722 dependencies:
1723 "@opentelemetry/core" "1.3.1"
1724
1725"@opentelemetry/propagator-jaeger@1.3.1":
1726 version "1.3.1"
1727 resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.3.1.tgz#ad02cf5e63f7adb6986418dac916e7b89c34df5b"
1728 integrity sha512-H6swQcjZ8aMCS5caZaEBaadfn205IqLlB3ZyY+tCWDf5YPwJgPpjw3qgYgWulHVSEzK7VQTle/mZG7u9MAe6Pw==
1729 dependencies:
1730 "@opentelemetry/core" "1.3.1"
1731
1732"@opentelemetry/resources@1.3.1", "@opentelemetry/resources@^1.3.1":
1733 version "1.3.1"
1734 resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.3.1.tgz#9fd85ac4ffeefc35441404b384d5c1db8b243121"
1735 integrity sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA==
1736 dependencies:
1737 "@opentelemetry/core" "1.3.1"
1738 "@opentelemetry/semantic-conventions" "1.3.1"
1739
1740"@opentelemetry/sdk-metrics-base@0.29.2", "@opentelemetry/sdk-metrics-base@~0.29.2":
1741 version "0.29.2"
1742 resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.29.2.tgz#bd515455f1d90e211458dcf957f0ae937772b155"
1743 integrity sha512-7hhhZ/6YRRgAXOUTeCsbe6SIk3wZAdAHnEwGGp7aiVH5AOyioHyHInw4EHtowlD6dbLxUWURjh6k+Geht2zbxg==
1744 dependencies:
1745 "@opentelemetry/api-metrics" "0.29.2"
1746 "@opentelemetry/core" "1.3.1"
1747 "@opentelemetry/resources" "1.3.1"
1748 lodash.merge "4.6.2"
1749
1750"@opentelemetry/sdk-trace-base@1.3.1", "@opentelemetry/sdk-trace-base@^1.3.1":
1751 version "1.3.1"
1752 resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz#958083dbab928eefd17848959ac8810c787bec7f"
1753 integrity sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==
1754 dependencies:
1755 "@opentelemetry/core" "1.3.1"
1756 "@opentelemetry/resources" "1.3.1"
1757 "@opentelemetry/semantic-conventions" "1.3.1"
1758
1759"@opentelemetry/sdk-trace-node@^1.3.1":
1760 version "1.3.1"
1761 resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.3.1.tgz#ef6598aef93b24bfaae1ddc0321d3cf00d44c304"
1762 integrity sha512-4sn/pYhaVaEI8WY0arivM77858IM5BjUKvymjJ+HmRNWBocCJKCCCY4P9cL8w8iCGGmst5yxecMyvM7OOFBnmg==
1763 dependencies:
1764 "@opentelemetry/context-async-hooks" "1.3.1"
1765 "@opentelemetry/core" "1.3.1"
1766 "@opentelemetry/propagator-b3" "1.3.1"
1767 "@opentelemetry/propagator-jaeger" "1.3.1"
1768 "@opentelemetry/sdk-trace-base" "1.3.1"
1769 semver "^7.3.5"
1770
1771"@opentelemetry/semantic-conventions@1.3.1", "@opentelemetry/semantic-conventions@^1.0.0", "@opentelemetry/semantic-conventions@^1.3.1":
1772 version "1.3.1"
1773 resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a"
1774 integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==
1775
1609"@peertube/feed@^5.0.1": 1776"@peertube/feed@^5.0.1":
1610 version "5.0.2" 1777 version "5.0.2"
1611 resolved "https://registry.yarnpkg.com/@peertube/feed/-/feed-5.0.2.tgz#d9ae7f38f1ccc75d353a5e24ad335a982bc4df74" 1778 resolved "https://registry.yarnpkg.com/@peertube/feed/-/feed-5.0.2.tgz#d9ae7f38f1ccc75d353a5e24ad335a982bc4df74"
@@ -1859,7 +2026,7 @@
1859 "@types/qs" "*" 2026 "@types/qs" "*"
1860 "@types/range-parser" "*" 2027 "@types/range-parser" "*"
1861 2028
1862"@types/express@*": 2029"@types/express@*", "@types/express@4.17.13":
1863 version "4.17.13" 2030 version "4.17.13"
1864 resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" 2031 resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
1865 integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== 2032 integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
@@ -2061,6 +2228,31 @@
2061 dependencies: 2228 dependencies:
2062 "@types/node" "*" 2229 "@types/node" "*"
2063 2230
2231"@types/pg-pool@2.0.3":
2232 version "2.0.3"
2233 resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.3.tgz#3eb8df2933f617f219a53091ad4080c94ba1c959"
2234 integrity sha512-fwK5WtG42Yb5RxAwxm3Cc2dJ39FlgcaNiXKvtTLAwtCn642X7dgel+w1+cLWwpSOFImR3YjsZtbkfjxbHtFAeg==
2235 dependencies:
2236 "@types/pg" "*"
2237
2238"@types/pg@*":
2239 version "8.6.5"
2240 resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.5.tgz#2dce9cb468a6a5e0f1296a59aea3ac75dd27b702"
2241 integrity sha512-tOkGtAqRVkHa/PVZicq67zuujI4Oorfglsr2IbKofDwBSysnaqSx7W1mDqFqdkGE6Fbgh+PZAl0r/BWON/mozw==
2242 dependencies:
2243 "@types/node" "*"
2244 pg-protocol "*"
2245 pg-types "^2.2.0"
2246
2247"@types/pg@8.6.1":
2248 version "8.6.1"
2249 resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.1.tgz#099450b8dc977e8197a44f5229cedef95c8747f9"
2250 integrity sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==
2251 dependencies:
2252 "@types/node" "*"
2253 pg-protocol "*"
2254 pg-types "^2.2.0"
2255
2064"@types/qs@*": 2256"@types/qs@*":
2065 version "6.9.7" 2257 version "6.9.7"
2066 resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" 2258 resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
@@ -2441,6 +2633,11 @@ ajv@^8.6.3:
2441 require-from-string "^2.0.2" 2633 require-from-string "^2.0.2"
2442 uri-js "^4.2.2" 2634 uri-js "^4.2.2"
2443 2635
2636ansi-color@^0.2.1:
2637 version "0.2.1"
2638 resolved "https://registry.yarnpkg.com/ansi-color/-/ansi-color-0.2.1.tgz#3e75c037475217544ed763a8db5709fa9ae5bf9a"
2639 integrity sha512-bF6xLaZBLpOQzgYUtYEhJx090nPSZk1BQ/q2oyBK9aMMcJHzx9uXGCjI2Y+LebsN4Jwoykr0V9whbPiogdyHoQ==
2640
2444ansi-colors@4.1.1: 2641ansi-colors@4.1.1:
2445 version "4.1.1" 2642 version "4.1.1"
2446 resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" 2643 resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
@@ -2977,6 +3174,16 @@ bufferutil@^4.0.3:
2977 dependencies: 3174 dependencies:
2978 node-gyp-build "^4.3.0" 3175 node-gyp-build "^4.3.0"
2979 3176
3177bufrw@^1.3.0:
3178 version "1.3.0"
3179 resolved "https://registry.yarnpkg.com/bufrw/-/bufrw-1.3.0.tgz#28d6cfdaf34300376836310f5c31d57eeb40c8fa"
3180 integrity sha512-jzQnSbdJqhIltU9O5KUiTtljP9ccw2u5ix59McQy4pV2xGhVLhRZIndY8GIrgh5HjXa6+QJ9AQhOd2QWQizJFQ==
3181 dependencies:
3182 ansi-color "^0.2.1"
3183 error "^7.0.0"
3184 hexer "^1.5.0"
3185 xtend "^4.0.0"
3186
2980bull@^4.1.0: 3187bull@^4.1.0:
2981 version "4.8.4" 3188 version "4.8.4"
2982 resolved "https://registry.yarnpkg.com/bull/-/bull-4.8.4.tgz#c538610492050d5160dbd9180704145f135a0aa9" 3189 resolved "https://registry.yarnpkg.com/bull/-/bull-4.8.4.tgz#c538610492050d5160dbd9180704145f135a0aa9"
@@ -4029,6 +4236,21 @@ error-ex@^1.3.1:
4029 dependencies: 4236 dependencies:
4030 is-arrayish "^0.2.1" 4237 is-arrayish "^0.2.1"
4031 4238
4239error@7.0.2:
4240 version "7.0.2"
4241 resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02"
4242 integrity sha512-UtVv4l5MhijsYUxPJo4390gzfZvAnTHreNnDjnTZaKIiZ/SemXxAhBkYSKtWa5RtBXbLP8tMgn/n0RUa/H7jXw==
4243 dependencies:
4244 string-template "~0.2.1"
4245 xtend "~4.0.0"
4246
4247error@^7.0.0:
4248 version "7.2.1"
4249 resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894"
4250 integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==
4251 dependencies:
4252 string-template "~0.2.1"
4253
4032es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: 4254es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5:
4033 version "1.20.1" 4255 version "1.20.1"
4034 resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" 4256 resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
@@ -5044,6 +5266,16 @@ helmet@^5.0.1:
5044 resolved "https://registry.yarnpkg.com/helmet/-/helmet-5.1.0.tgz#e98a5d4bf89ab8119c856018a3bcc82addadcd47" 5266 resolved "https://registry.yarnpkg.com/helmet/-/helmet-5.1.0.tgz#e98a5d4bf89ab8119c856018a3bcc82addadcd47"
5045 integrity sha512-klsunXs8rgNSZoaUrNeuCiWUxyc+wzucnEnFejUg3/A+CaF589k9qepLZZ1Jehnzig7YbD4hEuscGXuBY3fq+g== 5267 integrity sha512-klsunXs8rgNSZoaUrNeuCiWUxyc+wzucnEnFejUg3/A+CaF589k9qepLZZ1Jehnzig7YbD4hEuscGXuBY3fq+g==
5046 5268
5269hexer@^1.5.0:
5270 version "1.5.0"
5271 resolved "https://registry.yarnpkg.com/hexer/-/hexer-1.5.0.tgz#b86ce808598e8a9d1892c571f3cedd86fc9f0653"
5272 integrity sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg==
5273 dependencies:
5274 ansi-color "^0.2.1"
5275 minimist "^1.1.0"
5276 process "^0.10.0"
5277 xtend "^4.0.0"
5278
5047hexoid@1.0.0: 5279hexoid@1.0.0:
5048 version "1.0.0" 5280 version "1.0.0"
5049 resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" 5281 resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
@@ -5563,6 +5795,17 @@ isstream@0.1.x:
5563 resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 5795 resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
5564 integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== 5796 integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
5565 5797
5798jaeger-client@^3.15.0:
5799 version "3.19.0"
5800 resolved "https://registry.yarnpkg.com/jaeger-client/-/jaeger-client-3.19.0.tgz#9b5bd818ebd24e818616ee0f5cffe1722a53ae6e"
5801 integrity sha512-M0c7cKHmdyEUtjemnJyx/y9uX16XHocL46yQvyqDlPdvAcwPDbHrIbKjQdBqtiE4apQ/9dmr+ZLJYYPGnurgpw==
5802 dependencies:
5803 node-int64 "^0.4.0"
5804 opentracing "^0.14.4"
5805 thriftrw "^3.5.0"
5806 uuid "^8.3.2"
5807 xorshift "^1.1.1"
5808
5566jimp@^0.16.0: 5809jimp@^0.16.0:
5567 version "0.16.1" 5810 version "0.16.1"
5568 resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.16.1.tgz#192f851a30e5ca11112a3d0aa53137659a78ca7a" 5811 resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.16.1.tgz#192f851a30e5ca11112a3d0aa53137659a78ca7a"
@@ -5900,7 +6143,7 @@ lodash.isarguments@^3.1.0:
5900 resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 6143 resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
5901 integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== 6144 integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==
5902 6145
5903lodash.merge@^4.6.2: 6146lodash.merge@4.6.2, lodash.merge@^4.6.2:
5904 version "4.6.2" 6147 version "4.6.2"
5905 resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 6148 resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
5906 integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 6149 integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
@@ -5929,6 +6172,11 @@ logform@^2.3.2, logform@^2.4.0:
5929 safe-stable-stringify "^2.3.1" 6172 safe-stable-stringify "^2.3.1"
5930 triple-beam "^1.3.0" 6173 triple-beam "^1.3.0"
5931 6174
6175long@^2.4.0:
6176 version "2.4.0"
6177 resolved "https://registry.yarnpkg.com/long/-/long-2.4.0.tgz#9fa180bb1d9500cdc29c4156766a1995e1f4524f"
6178 integrity sha512-ijUtjmO/n2A5PaosNG9ZGDsQ3vxJg7ZW8vsY8Kp0f2yIZWhSJvjmegV7t+9RPQKxKrvj8yKGehhS+po14hPLGQ==
6179
5932loose-envify@^1.0.0: 6180loose-envify@^1.0.0:
5933 version "1.4.0" 6181 version "1.4.0"
5934 resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 6182 resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@@ -6320,6 +6568,11 @@ mocha@^10.0.0:
6320 yargs-parser "20.2.4" 6568 yargs-parser "20.2.4"
6321 yargs-unparser "2.0.0" 6569 yargs-unparser "2.0.0"
6322 6570
6571module-details-from-path@^1.0.3:
6572 version "1.0.3"
6573 resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b"
6574 integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==
6575
6323moment-timezone@^0.5.34: 6576moment-timezone@^0.5.34:
6324 version "0.5.34" 6577 version "0.5.34"
6325 resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c" 6578 resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
@@ -6556,6 +6809,11 @@ node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-build@^4.3.0:
6556 resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" 6809 resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4"
6557 integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== 6810 integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==
6558 6811
6812node-int64@^0.4.0:
6813 version "0.4.0"
6814 resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
6815 integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
6816
6559node-media-server@^2.1.4: 6817node-media-server@^2.1.4:
6560 version "2.3.12" 6818 version "2.3.12"
6561 resolved "https://registry.yarnpkg.com/node-media-server/-/node-media-server-2.3.12.tgz#5380a60a26545144fa1e7954f08ece6f255d28b4" 6819 resolved "https://registry.yarnpkg.com/node-media-server/-/node-media-server-2.3.12.tgz#5380a60a26545144fa1e7954f08ece6f255d28b4"
@@ -6713,6 +6971,11 @@ open@7:
6713 is-docker "^2.0.0" 6971 is-docker "^2.0.0"
6714 is-wsl "^2.1.1" 6972 is-wsl "^2.1.1"
6715 6973
6974opentracing@^0.14.4:
6975 version "0.14.7"
6976 resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.7.tgz#25d472bd0296dc0b64d7b94cbc995219031428f5"
6977 integrity sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q==
6978
6716optionator@^0.9.1: 6979optionator@^0.9.1:
6717 version "0.9.1" 6980 version "0.9.1"
6718 resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" 6981 resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@@ -6998,12 +7261,12 @@ pg-pool@^3.5.1:
6998 resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.1.tgz#f499ce76f9bf5097488b3b83b19861f28e4ed905" 7261 resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.1.tgz#f499ce76f9bf5097488b3b83b19861f28e4ed905"
6999 integrity sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ== 7262 integrity sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==
7000 7263
7001pg-protocol@^1.5.0: 7264pg-protocol@*, pg-protocol@^1.5.0:
7002 version "1.5.0" 7265 version "1.5.0"
7003 resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0" 7266 resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0"
7004 integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ== 7267 integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==
7005 7268
7006pg-types@^2.1.0: 7269pg-types@^2.1.0, pg-types@^2.2.0:
7007 version "2.2.0" 7270 version "2.2.0"
7008 resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" 7271 resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
7009 integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== 7272 integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
@@ -7148,6 +7411,11 @@ process-nextick-args@~2.0.0:
7148 resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 7411 resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
7149 integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 7412 integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
7150 7413
7414process@^0.10.0:
7415 version "0.10.1"
7416 resolved "https://registry.yarnpkg.com/process/-/process-0.10.1.tgz#842457cc51cfed72dc775afeeafb8c6034372725"
7417 integrity sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA==
7418
7151process@^0.11.10: 7419process@^0.11.10:
7152 version "0.11.10" 7420 version "0.11.10"
7153 resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" 7421 resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
@@ -7618,6 +7886,15 @@ require-from-string@^2.0.2:
7618 resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" 7886 resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
7619 integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== 7887 integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
7620 7888
7889require-in-the-middle@^5.0.3:
7890 version "5.1.0"
7891 resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz#b768f800377b47526d026bbf5a7f727f16eb412f"
7892 integrity sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ==
7893 dependencies:
7894 debug "^4.1.1"
7895 module-details-from-path "^1.0.3"
7896 resolve "^1.12.0"
7897
7621require-main-filename@^2.0.0: 7898require-main-filename@^2.0.0:
7622 version "2.0.0" 7899 version "2.0.0"
7623 resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" 7900 resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
@@ -7647,7 +7924,7 @@ resolve-tspaths@^0.6.0:
7647 commander "9.2.0" 7924 commander "9.2.0"
7648 fast-glob "3.2.11" 7925 fast-glob "3.2.11"
7649 7926
7650resolve@^1.10.1, resolve@^1.15.1, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.22.0: 7927resolve@^1.10.1, resolve@^1.12.0, resolve@^1.15.1, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.22.0:
7651 version "1.22.1" 7928 version "1.22.1"
7652 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 7929 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
7653 integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 7930 integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
@@ -7910,6 +8187,11 @@ shell-quote@^1.7.3:
7910 resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" 8187 resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
7911 integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== 8188 integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==
7912 8189
8190shimmer@^1.2.1:
8191 version "1.2.1"
8192 resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337"
8193 integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==
8194
7913short-uuid@^4.2.0: 8195short-uuid@^4.2.0:
7914 version "4.2.0" 8196 version "4.2.0"
7915 resolved "https://registry.yarnpkg.com/short-uuid/-/short-uuid-4.2.0.tgz#3706d9e7287ac589dc5ffe324d3e34817a07540b" 8197 resolved "https://registry.yarnpkg.com/short-uuid/-/short-uuid-4.2.0.tgz#3706d9e7287ac589dc5ffe324d3e34817a07540b"
@@ -8258,6 +8540,11 @@ string-argv@^0.1.1:
8258 resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" 8540 resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738"
8259 integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== 8541 integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==
8260 8542
8543string-template@~0.2.1:
8544 version "0.2.1"
8545 resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
8546 integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==
8547
8261"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: 8548"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
8262 version "4.2.3" 8549 version "4.2.3"
8263 resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 8550 resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -8431,6 +8718,15 @@ thirty-two@^1.0.2:
8431 resolved "https://registry.yarnpkg.com/thirty-two/-/thirty-two-1.0.2.tgz#4ca2fffc02a51290d2744b9e3f557693ca6b627a" 8718 resolved "https://registry.yarnpkg.com/thirty-two/-/thirty-two-1.0.2.tgz#4ca2fffc02a51290d2744b9e3f557693ca6b627a"
8432 integrity sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA== 8719 integrity sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==
8433 8720
8721thriftrw@^3.5.0:
8722 version "3.12.0"
8723 resolved "https://registry.yarnpkg.com/thriftrw/-/thriftrw-3.12.0.tgz#30857847755e7f036b2e0a79d11c9f55075539d9"
8724 integrity sha512-4YZvR4DPEI41n4Opwr4jmrLGG4hndxr7387kzRFIIzxHQjarPusH4lGXrugvgb7TtPrfZVTpZCVe44/xUxowEw==
8725 dependencies:
8726 bufrw "^1.3.0"
8727 error "7.0.2"
8728 long "^2.4.0"
8729
8434through2@^0.6.3, through2@~0.6.1: 8730through2@^0.6.3, through2@~0.6.1:
8435 version "0.6.5" 8731 version "0.6.5"
8436 resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" 8732 resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48"
@@ -9202,7 +9498,12 @@ xmlhttprequest-ssl@~2.0.0:
9202 resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" 9498 resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
9203 integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== 9499 integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
9204 9500
9205"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.1: 9501xorshift@^1.1.1:
9502 version "1.2.0"
9503 resolved "https://registry.yarnpkg.com/xorshift/-/xorshift-1.2.0.tgz#30a4cdd8e9f8d09d959ed2a88c42a09c660e8148"
9504 integrity sha512-iYgNnGyeeJ4t6U11NpA/QiKy+PXn5Aa3Azg5qkwIFz1tBLllQrjjsk9yzD7IAK0naNU4JxdeDgqW9ov4u/hc4g==
9505
9506"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1:
9206 version "4.0.2" 9507 version "4.0.2"
9207 resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 9508 resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
9208 integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 9509 integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==