mirror of
https://github.com/owncast/owncast.git
synced 2024-11-21 20:28:15 +03:00
* Add support for established user mode. #1587 * Tweak tests * Tweak tests * Update test * Fix test.
This commit is contained in:
parent
123d559ba4
commit
e0a75d5d54
10 changed files with 245 additions and 83 deletions
|
@ -1,6 +1,10 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import "github.com/owncast/owncast/models"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/owncast/owncast/models"
|
||||||
|
)
|
||||||
|
|
||||||
// Defaults will hold default configuration values.
|
// Defaults will hold default configuration values.
|
||||||
type Defaults struct {
|
type Defaults struct {
|
||||||
|
@ -27,6 +31,8 @@ type Defaults struct {
|
||||||
|
|
||||||
FederationUsername string
|
FederationUsername string
|
||||||
FederationGoLiveMessage string
|
FederationGoLiveMessage string
|
||||||
|
|
||||||
|
ChatEstablishedUserModeTimeDuration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaults will return default configuration values.
|
// GetDefaults will return default configuration values.
|
||||||
|
@ -54,6 +60,8 @@ func GetDefaults() Defaults {
|
||||||
RTMPServerPort: 1935,
|
RTMPServerPort: 1935,
|
||||||
StreamKey: "abc123",
|
StreamKey: "abc123",
|
||||||
|
|
||||||
|
ChatEstablishedUserModeTimeDuration: time.Minute * 15,
|
||||||
|
|
||||||
StreamVariants: []models.StreamOutputVariant{
|
StreamVariants: []models.StreamOutputVariant{
|
||||||
{
|
{
|
||||||
IsAudioPassthrough: true,
|
IsAudioPassthrough: true,
|
||||||
|
|
|
@ -345,3 +345,24 @@ func SendChatAction(integration user.ExternalAPIUser, w http.ResponseWriter, r *
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "sent")
|
controllers.WriteSimpleResponse(w, true, "sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetEnableEstablishedChatUserMode sets the requirement for a chat user
|
||||||
|
// to be "established" for some time before taking part in chat.
|
||||||
|
func SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !requirePOST(w, r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
configValue, success := getValueFromRequest(w, r)
|
||||||
|
if !success {
|
||||||
|
controllers.WriteSimpleResponse(w, false, "unable to update chat established user only mode")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := data.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil {
|
||||||
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
controllers.WriteSimpleResponse(w, true, "chat established users only mode updated")
|
||||||
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
ChatDisabled: data.GetChatDisabled(),
|
ChatDisabled: data.GetChatDisabled(),
|
||||||
ChatJoinMessagesEnabled: data.GetChatJoinMessagesEnabled(),
|
ChatJoinMessagesEnabled: data.GetChatJoinMessagesEnabled(),
|
||||||
SocketHostOverride: data.GetWebsocketOverrideHost(),
|
SocketHostOverride: data.GetWebsocketOverrideHost(),
|
||||||
|
ChatEstablishedUserMode: data.GetChatEstbalishedUsersOnlyMode(),
|
||||||
VideoSettings: videoSettings{
|
VideoSettings: videoSettings{
|
||||||
VideoQualityVariants: videoQualityVariants,
|
VideoQualityVariants: videoQualityVariants,
|
||||||
LatencyLevel: data.GetStreamLatencyLevel().Level,
|
LatencyLevel: data.GetStreamLatencyLevel().Level,
|
||||||
|
@ -98,6 +99,7 @@ type serverConfigAdminResponse struct {
|
||||||
YP yp `json:"yp"`
|
YP yp `json:"yp"`
|
||||||
ChatDisabled bool `json:"chatDisabled"`
|
ChatDisabled bool `json:"chatDisabled"`
|
||||||
ChatJoinMessagesEnabled bool `json:"chatJoinMessagesEnabled"`
|
ChatJoinMessagesEnabled bool `json:"chatJoinMessagesEnabled"`
|
||||||
|
ChatEstablishedUserMode bool `json:"chatEstablishedUserMode"`
|
||||||
ExternalActions []models.ExternalAction `json:"externalActions"`
|
ExternalActions []models.ExternalAction `json:"externalActions"`
|
||||||
SupportedCodecs []string `json:"supportedCodecs"`
|
SupportedCodecs []string `json:"supportedCodecs"`
|
||||||
VideoCodec string `json:"videoCodec"`
|
VideoCodec string `json:"videoCodec"`
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
|
@ -330,6 +331,16 @@ func SendActionToUser(userID string, text string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) eventReceived(event chatClientEvent) {
|
func (s *Server) eventReceived(event chatClientEvent) {
|
||||||
|
c := event.client
|
||||||
|
u := c.User
|
||||||
|
|
||||||
|
// If established chat user only mode is enabled and the user is not old
|
||||||
|
// enough then reject this event and send them an informative message.
|
||||||
|
if u != nil && data.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
|
||||||
|
s.sendActionToClient(c, "You have not been an established chat participant long enough to take part in chat. Please enjoy the stream and try again later.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var typecheck map[string]interface{}
|
var typecheck map[string]interface{}
|
||||||
if err := json.Unmarshal(event.data, &typecheck); err != nil {
|
if err := json.Unmarshal(event.data, &typecheck); err != nil {
|
||||||
log.Debugln(err)
|
log.Debugln(err)
|
||||||
|
|
|
@ -14,47 +14,48 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
extraContentKey = "extra_page_content"
|
extraContentKey = "extra_page_content"
|
||||||
streamTitleKey = "stream_title"
|
streamTitleKey = "stream_title"
|
||||||
streamKeyKey = "stream_key"
|
streamKeyKey = "stream_key"
|
||||||
logoPathKey = "logo_path"
|
logoPathKey = "logo_path"
|
||||||
serverSummaryKey = "server_summary"
|
serverSummaryKey = "server_summary"
|
||||||
serverWelcomeMessageKey = "server_welcome_message"
|
serverWelcomeMessageKey = "server_welcome_message"
|
||||||
serverNameKey = "server_name"
|
serverNameKey = "server_name"
|
||||||
serverURLKey = "server_url"
|
serverURLKey = "server_url"
|
||||||
httpPortNumberKey = "http_port_number"
|
httpPortNumberKey = "http_port_number"
|
||||||
httpListenAddressKey = "http_listen_address"
|
httpListenAddressKey = "http_listen_address"
|
||||||
websocketHostOverrideKey = "websocket_host_override"
|
websocketHostOverrideKey = "websocket_host_override"
|
||||||
rtmpPortNumberKey = "rtmp_port_number"
|
rtmpPortNumberKey = "rtmp_port_number"
|
||||||
serverMetadataTagsKey = "server_metadata_tags"
|
serverMetadataTagsKey = "server_metadata_tags"
|
||||||
directoryEnabledKey = "directory_enabled"
|
directoryEnabledKey = "directory_enabled"
|
||||||
directoryRegistrationKeyKey = "directory_registration_key"
|
directoryRegistrationKeyKey = "directory_registration_key"
|
||||||
socialHandlesKey = "social_handles"
|
socialHandlesKey = "social_handles"
|
||||||
peakViewersSessionKey = "peak_viewers_session"
|
peakViewersSessionKey = "peak_viewers_session"
|
||||||
peakViewersOverallKey = "peak_viewers_overall"
|
peakViewersOverallKey = "peak_viewers_overall"
|
||||||
lastDisconnectTimeKey = "last_disconnect_time"
|
lastDisconnectTimeKey = "last_disconnect_time"
|
||||||
ffmpegPathKey = "ffmpeg_path"
|
ffmpegPathKey = "ffmpeg_path"
|
||||||
nsfwKey = "nsfw"
|
nsfwKey = "nsfw"
|
||||||
s3StorageEnabledKey = "s3_storage_enabled"
|
s3StorageEnabledKey = "s3_storage_enabled"
|
||||||
s3StorageConfigKey = "s3_storage_config"
|
s3StorageConfigKey = "s3_storage_config"
|
||||||
videoLatencyLevel = "video_latency_level"
|
videoLatencyLevel = "video_latency_level"
|
||||||
videoStreamOutputVariantsKey = "video_stream_output_variants"
|
videoStreamOutputVariantsKey = "video_stream_output_variants"
|
||||||
chatDisabledKey = "chat_disabled"
|
chatDisabledKey = "chat_disabled"
|
||||||
externalActionsKey = "external_actions"
|
externalActionsKey = "external_actions"
|
||||||
customStylesKey = "custom_styles"
|
customStylesKey = "custom_styles"
|
||||||
videoCodecKey = "video_codec"
|
videoCodecKey = "video_codec"
|
||||||
blockedUsernamesKey = "blocked_usernames"
|
blockedUsernamesKey = "blocked_usernames"
|
||||||
publicKeyKey = "public_key"
|
publicKeyKey = "public_key"
|
||||||
privateKeyKey = "private_key"
|
privateKeyKey = "private_key"
|
||||||
serverInitDateKey = "server_init_date"
|
serverInitDateKey = "server_init_date"
|
||||||
federationEnabledKey = "federation_enabled"
|
federationEnabledKey = "federation_enabled"
|
||||||
federationUsernameKey = "federation_username"
|
federationUsernameKey = "federation_username"
|
||||||
federationPrivateKey = "federation_private"
|
federationPrivateKey = "federation_private"
|
||||||
federationGoLiveMessageKey = "federation_go_live_message"
|
federationGoLiveMessageKey = "federation_go_live_message"
|
||||||
federationShowEngagementKey = "federation_show_engagement"
|
federationShowEngagementKey = "federation_show_engagement"
|
||||||
federationBlockedDomainsKey = "federation_blocked_domains"
|
federationBlockedDomainsKey = "federation_blocked_domains"
|
||||||
suggestedUsernamesKey = "suggested_usernames"
|
suggestedUsernamesKey = "suggested_usernames"
|
||||||
chatJoinMessagesEnabledKey = "chat_join_messages_enabled"
|
chatJoinMessagesEnabledKey = "chat_join_messages_enabled"
|
||||||
|
chatEstablishedUsersOnlyModeKey = "chat_established_users_only_mode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetExtraPageBodyContent will return the user-supplied body content.
|
// GetExtraPageBodyContent will return the user-supplied body content.
|
||||||
|
@ -501,6 +502,21 @@ func GetChatDisabled() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatEstablishedUsersOnlyMode sets the state of established user only mode.
|
||||||
|
func SetChatEstablishedUsersOnlyMode(enabled bool) error {
|
||||||
|
return _datastore.SetBool(chatEstablishedUsersOnlyModeKey, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatEstbalishedUsersOnlyMode returns the state of established user only mode.
|
||||||
|
func GetChatEstbalishedUsersOnlyMode() bool {
|
||||||
|
enabled, err := _datastore.GetBool(chatEstablishedUsersOnlyModeKey)
|
||||||
|
if err == nil {
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// GetExternalActions will return the registered external actions.
|
// GetExternalActions will return the registered external actions.
|
||||||
func GetExternalActions() []models.ExternalAction {
|
func GetExternalActions() []models.ExternalAction {
|
||||||
configEntry, err := _datastore.Get(externalActionsKey)
|
configEntry, err := _datastore.Get(externalActionsKey)
|
||||||
|
|
|
@ -177,6 +177,9 @@ func Start() error {
|
||||||
// Disable chat user join messages
|
// Disable chat user join messages
|
||||||
http.HandleFunc("/api/admin/config/chat/joinmessagesenabled", middleware.RequireAdminAuth(admin.SetChatJoinMessagesEnabled))
|
http.HandleFunc("/api/admin/config/chat/joinmessagesenabled", middleware.RequireAdminAuth(admin.SetChatJoinMessagesEnabled))
|
||||||
|
|
||||||
|
// Enable/disable chat established user mode
|
||||||
|
http.HandleFunc("/api/admin/config/chat/establishedusermode", middleware.RequireAdminAuth(admin.SetEnableEstablishedChatUserMode))
|
||||||
|
|
||||||
// Set chat usernames that are not allowed
|
// Set chat usernames that are not allowed
|
||||||
http.HandleFunc("/api/admin/config/chat/forbiddenusernames", middleware.RequireAdminAuth(admin.SetForbiddenUsernameList))
|
http.HandleFunc("/api/admin/config/chat/forbiddenusernames", middleware.RequireAdminAuth(admin.SetForbiddenUsernameList))
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,12 @@ test('can fetch chat messages', async (done) => {
|
||||||
.auth('admin', 'abc123')
|
.auth('admin', 'abc123')
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const expectedBody = `${testMessage.body}`;
|
const message = res.body.filter((m) => m.body === testMessage.body)[0];
|
||||||
const message = res.body.filter(function (msg) {
|
if (!message) {
|
||||||
return msg.body === expectedBody;
|
throw new Error('Message not found');
|
||||||
})[0];
|
}
|
||||||
|
|
||||||
|
const expectedBody = testMessage.body;
|
||||||
|
|
||||||
expect(message.body).toBe(expectedBody);
|
expect(message.body).toBe(expectedBody);
|
||||||
expect(message.user.displayName).toBe(userDisplayName);
|
expect(message.user.displayName).toBe(userDisplayName);
|
||||||
|
@ -52,7 +54,7 @@ test('can derive display name from user header', async (done) => {
|
||||||
test('can overwrite user header derived display name with body', async (done) => {
|
test('can overwrite user header derived display name with body', async (done) => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.post('/api/chat/register')
|
.post('/api/chat/register')
|
||||||
.send({displayName: 'TestUserChat'})
|
.send({ displayName: 'TestUserChat' })
|
||||||
.set('X-Forwarded-User', 'test-user')
|
.set('X-Forwarded-User', 'test-user')
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,19 @@ const WebSocket = require('ws');
|
||||||
|
|
||||||
const registerChat = require('./lib/chat').registerChat;
|
const registerChat = require('./lib/chat').registerChat;
|
||||||
const sendChatMessage = require('./lib/chat').sendChatMessage;
|
const sendChatMessage = require('./lib/chat').sendChatMessage;
|
||||||
const listenForEvent = require('./lib/chat').listenForEvent;
|
|
||||||
|
|
||||||
const testVisibilityMessage = {
|
const testVisibilityMessage = {
|
||||||
body: 'message ' + Math.floor(Math.random() * 100),
|
body: 'message ' + Math.floor(Math.random() * 100),
|
||||||
type: 'CHAT',
|
type: 'CHAT',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var messageId;
|
||||||
|
|
||||||
|
const establishedUserFailedChatMessage = {
|
||||||
|
body: 'this message should fail to send ' + Math.floor(Math.random() * 100),
|
||||||
|
type: 'CHAT',
|
||||||
|
};
|
||||||
|
|
||||||
test('can send a chat message', async (done) => {
|
test('can send a chat message', async (done) => {
|
||||||
const registration = await registerChat();
|
const registration = await registerChat();
|
||||||
const accessToken = registration.accessToken;
|
const accessToken = registration.accessToken;
|
||||||
|
@ -30,14 +36,14 @@ test('verify we can make API call to mark message as hidden', async (done) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify the visibility change comes through the websocket
|
// Verify the visibility change comes through the websocket
|
||||||
ws.on('message', function incoming(message) {
|
ws.on('message', async function incoming(message) {
|
||||||
const messages = message.split('\n');
|
const messages = message.split('\n');
|
||||||
messages.forEach(function (message) {
|
messages.forEach(async function (message) {
|
||||||
const event = JSON.parse(message);
|
const event = JSON.parse(message);
|
||||||
|
|
||||||
if (event.type === 'VISIBILITY-UPDATE') {
|
if (event.type === 'VISIBILITY-UPDATE') {
|
||||||
done();
|
|
||||||
ws.close();
|
ws.close();
|
||||||
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -48,7 +54,7 @@ test('verify we can make API call to mark message as hidden', async (done) => {
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const message = res.body[0];
|
const message = res.body[0];
|
||||||
const messageId = message.id;
|
messageId = message.id;
|
||||||
await request
|
await request
|
||||||
.post('/api/admin/chat/updatemessagevisibility')
|
.post('/api/admin/chat/updatemessagevisibility')
|
||||||
.auth('admin', 'abc123')
|
.auth('admin', 'abc123')
|
||||||
|
@ -57,15 +63,56 @@ test('verify we can make API call to mark message as hidden', async (done) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify message has become hidden', async (done) => {
|
test('verify message has become hidden', async (done) => {
|
||||||
|
await new Promise((r) => setTimeout(r, 2000));
|
||||||
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.get('/api/admin/chat/messages')
|
.get('/api/admin/chat/messages')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.auth('admin', 'abc123');
|
.auth('admin', 'abc123');
|
||||||
|
|
||||||
const message = res.body.filter((obj) => {
|
const message = res.body.filter((obj) => {
|
||||||
return obj.body === `${testVisibilityMessage.body}`;
|
return obj.id === messageId;
|
||||||
});
|
});
|
||||||
expect(message.length).toBe(1);
|
expect(message.length).toBe(1);
|
||||||
// expect(message[0].hiddenAt).toBeTruthy();
|
// expect(message[0].hiddenAt).toBeTruthy();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('can enable established chat user mode', async (done) => {
|
||||||
|
await request
|
||||||
|
.post('/api/admin/config/chat/establishedusermode')
|
||||||
|
.auth('admin', 'abc123')
|
||||||
|
.send({ value: true })
|
||||||
|
.expect(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can send a message after established user mode is enabled', async (done) => {
|
||||||
|
const registration = await registerChat();
|
||||||
|
const accessToken = registration.accessToken;
|
||||||
|
|
||||||
|
sendChatMessage(establishedUserFailedChatMessage, accessToken, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verify rejected message is not in the chat feed', async (done) => {
|
||||||
|
const res = await request
|
||||||
|
.get('/api/admin/chat/messages')
|
||||||
|
.expect(200)
|
||||||
|
.auth('admin', 'abc123');
|
||||||
|
|
||||||
|
const message = res.body.filter((obj) => {
|
||||||
|
return obj.body === establishedUserFailedChatMessage.body;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(message.length).toBe(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can disable established chat user mode', async (done) => {
|
||||||
|
await request
|
||||||
|
.post('/api/admin/config/chat/establishedusermode')
|
||||||
|
.auth('admin', 'abc123')
|
||||||
|
.send({ value: false })
|
||||||
|
.expect(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
|
@ -11,16 +11,11 @@ async function registerChat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendChatMessage(message, accessToken, done) {
|
async function sendChatMessage(message, accessToken, done) {
|
||||||
const ws = new WebSocket(
|
const ws = new WebSocket(`ws://localhost:8080/ws?accessToken=${accessToken}`);
|
||||||
`ws://localhost:8080/ws?accessToken=${accessToken}`,
|
|
||||||
{
|
|
||||||
origin: 'http://localhost:8080',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function onOpen() {
|
async function onOpen() {
|
||||||
ws.send(JSON.stringify(message), function () {
|
ws.send(JSON.stringify(message), async function () {
|
||||||
ws.close();
|
ws.close();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -30,12 +25,7 @@ function sendChatMessage(message, accessToken, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listenForEvent(name, accessToken, done) {
|
async function listenForEvent(name, accessToken, done) {
|
||||||
const ws = new WebSocket(
|
const ws = new WebSocket(`ws://localhost:8080/ws?accessToken=${accessToken}`);
|
||||||
`ws://localhost:8080/ws?accessToken=${accessToken}`,
|
|
||||||
{
|
|
||||||
origin: 'http://localhost:8080',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ws.on('message', function incoming(message) {
|
ws.on('message', function incoming(message) {
|
||||||
const messages = message.split('\n');
|
const messages = message.split('\n');
|
||||||
|
|
92
test/automated/api/package-lock.json
generated
92
test/automated/api/package-lock.json
generated
|
@ -600,6 +600,15 @@
|
||||||
"node": ">= 10.14.2"
|
"node": ">= 10.14.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jest/core/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jest/core/node_modules/strip-ansi": {
|
"node_modules/@jest/core/node_modules/strip-ansi": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
@ -984,15 +993,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
@ -3373,6 +3373,15 @@
|
||||||
"node": ">= 10.14.2"
|
"node": ">= 10.14.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-runtime/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-runtime/node_modules/cliui": {
|
"node_modules/jest-runtime/node_modules/cliui": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
@ -3638,6 +3647,15 @@
|
||||||
"node": ">= 10.13.0"
|
"node": ">= 10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest/node_modules/cliui": {
|
"node_modules/jest/node_modules/cliui": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
@ -4613,6 +4631,15 @@
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pretty-format/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prompts": {
|
"node_modules/prompts": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
|
||||||
|
@ -5634,6 +5661,15 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-length/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-length/node_modules/strip-ansi": {
|
"node_modules/string-length/node_modules/strip-ansi": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
@ -6791,6 +6827,12 @@
|
||||||
"strip-ansi": "^6.0.0"
|
"strip-ansi": "^6.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
@ -7132,12 +7174,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
@ -8688,6 +8724,12 @@
|
||||||
"jest-cli": "^26.6.3"
|
"jest-cli": "^26.6.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
@ -9211,6 +9253,12 @@
|
||||||
"yargs": "^15.4.1"
|
"yargs": "^15.4.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
@ -10062,6 +10110,14 @@
|
||||||
"ansi-regex": "^5.0.0",
|
"ansi-regex": "^5.0.0",
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
"react-is": "^17.0.1"
|
"react-is": "^17.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"prompts": {
|
"prompts": {
|
||||||
|
@ -10907,6 +10963,12 @@
|
||||||
"strip-ansi": "^6.0.0"
|
"strip-ansi": "^6.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
|
Loading…
Reference in a new issue