Add support for blocklisting e-mail domains.

E-mails in the domain blocklist are disallowed on the admin UI, public
subscription forms, API, and in the bulk importer.

- Add blocklist setting that takes a list of multi-line domains on the
  Settings -> Privacy UI.
- Refactor e-mail validation in subimporter to add blocklist checking
  centrally.
- Add Cypress testr testing domain blocklist behaviour on admin
  and non-admin views.

Closes #336.
This commit is contained in:
Kailash Nadh 2021-09-25 12:57:55 +05:30
parent 9f3eb7e4a4
commit 7aee36eab1
28 changed files with 572 additions and 452 deletions

View file

@ -6,10 +6,8 @@ import (
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
"github.com/knadh/listmonk/internal/subimporter"
"github.com/knadh/listmonk/models"
"github.com/labstack/echo"
"github.com/lib/pq"
@ -164,12 +162,12 @@ func handleBounceWebhook(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.Ts("globals.messages.invalidData"))
}
if err := validateBounceFields(b, app); err != nil {
if bv, err := validateBounceFields(b, app); err != nil {
return err
} else {
b = bv
}
b.Email = strings.ToLower(b.Email)
if len(b.Meta) == 0 {
b.Meta = json.RawMessage("{}")
}
@ -234,22 +232,26 @@ func handleBounceWebhook(c echo.Context) error {
return c.JSON(http.StatusOK, okResp{true})
}
func validateBounceFields(b models.Bounce, app *App) error {
func validateBounceFields(b models.Bounce, app *App) (models.Bounce, error) {
if b.Email == "" && b.SubscriberUUID == "" {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidData"))
}
if b.Type != models.BounceTypeHard && b.Type != models.BounceTypeSoft {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidData"))
}
if b.Email != "" && !subimporter.IsEmail(b.Email) {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidEmail"))
return b, echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidData"))
}
if b.SubscriberUUID != "" && !reUUID.MatchString(b.SubscriberUUID) {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidUUID"))
return b, echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidUUID"))
}
return nil
if b.Email != "" {
em, err := app.importer.SanitizeEmail(b.Email)
if err != nil {
return b, echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
b.Email = em
}
if b.Type != models.BounceTypeHard && b.Type != models.BounceTypeSoft {
return b, echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidData"))
}
return b, nil
}

View file

@ -15,7 +15,6 @@ import (
"github.com/gofrs/uuid"
"github.com/jmoiron/sqlx"
"github.com/knadh/listmonk/internal/subimporter"
"github.com/knadh/listmonk/models"
"github.com/labstack/echo"
"github.com/lib/pq"
@ -687,7 +686,7 @@ func validateCampaignFields(c campaignReq, app *App) (campaignReq, error) {
if c.FromEmail == "" {
c.FromEmail = app.constants.FromEmail
} else if !regexFromAddress.Match([]byte(c.FromEmail)) {
if !subimporter.IsEmail(c.FromEmail) {
if _, err := app.importer.SanitizeEmail(c.FromEmail); err != nil {
return c, errors.New(app.i18n.T("campaigns.fieldInvalidFromEmail"))
}
}

View file

@ -61,6 +61,7 @@ type constants struct {
AllowExport bool `koanf:"allow_export"`
AllowWipe bool `koanf:"allow_wipe"`
Exportable map[string]bool `koanf:"-"`
DomainBlocklist map[string]bool `koanf:"-"`
} `koanf:"privacy"`
AdminUsername []byte `koanf:"admin_username"`
AdminPassword []byte `koanf:"admin_password"`
@ -291,6 +292,7 @@ func initConstants() *constants {
c.Lang = ko.String("app.lang")
c.Privacy.Exportable = maps.StringSliceToLookupMap(ko.Strings("privacy.exportable"))
c.MediaProvider = ko.String("upload.provider")
c.Privacy.DomainBlocklist = maps.StringSliceToLookupMap(ko.Strings("privacy.domain_blocklist"))
// Static URLS.
// url.com/subscription/{campaign_uuid}/{subscriber_uuid}
@ -366,6 +368,7 @@ func initCampaignManager(q *Queries, cs *constants, app *App) *manager.Manager {
func initImporter(q *Queries, db *sqlx.DB, app *App) *subimporter.Importer {
return subimporter.New(
subimporter.Options{
DomainBlocklist: app.constants.Privacy.DomainBlocklist,
UpsertStmt: q.UpsertSubscriber.Stmt,
BlocklistStmt: q.UpsertBlocklistSubscriber.Stmt,
UpdateListDateStmt: q.UpdateListsDate.Stmt,
@ -373,7 +376,7 @@ func initImporter(q *Queries, db *sqlx.DB, app *App) *subimporter.Importer {
app.sendNotification(app.constants.NotifyEmails, subject, notifTplImport, data)
return nil
},
}, db.DB)
}, db.DB, app.i18n)
}
// initSMTPMessenger initializes the SMTP messenger.

View file

@ -318,15 +318,17 @@ func handleSubscriptionForm(c echo.Context) error {
}
// If there's no name, use the name bit from the e-mail.
req.Email = strings.ToLower(req.Email)
req.Name = strings.TrimSpace(req.Name)
if req.Name == "" {
req.Name = strings.Split(req.Email, "@")[0]
}
// Validate fields.
if err := subimporter.ValidateFields(req.SubReq); err != nil {
if r, err := app.importer.ValidateFields(req.SubReq); err != nil {
return c.Render(http.StatusInternalServerError, tplMessage,
makeMsgTpl(app.i18n.T("public.errorTitle"), "", err.Error()))
} else {
req.SubReq = r
}
// Insert the subscriber into the DB.

View file

@ -39,6 +39,7 @@ type settings struct {
PrivacyAllowExport bool `json:"privacy.allow_export"`
PrivacyAllowWipe bool `json:"privacy.allow_wipe"`
PrivacyExportable []string `json:"privacy.exportable"`
DomainBlocklist []string `json:"privacy.domain_blocklist"`
UploadProvider string `json:"upload.provider"`
UploadFilesystemUploadPath string `json:"upload.filesystem.upload_path"`
@ -246,6 +247,16 @@ func handleUpdateSettings(c echo.Context) error {
set.SendgridKey = cur.SendgridKey
}
// Domain blocklist.
doms := make([]string, 0)
for _, d := range set.DomainBlocklist {
d = strings.TrimSpace(strings.ToLower(d))
if d != "" {
doms = append(doms, d)
}
}
set.DomainBlocklist = doms
// Marshal settings.
b, err := json.Marshal(set)
if err != nil {

View file

@ -293,9 +293,12 @@ func handleCreateSubscriber(c echo.Context) error {
if err := c.Bind(&req); err != nil {
return err
}
req.Email = strings.ToLower(strings.TrimSpace(req.Email))
if err := subimporter.ValidateFields(req); err != nil {
r, err := app.importer.ValidateFields(req)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} else {
req = r
}
// Insert the subscriber into the DB.
@ -325,9 +328,13 @@ func handleUpdateSubscriber(c echo.Context) error {
if id < 1 {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidID"))
}
if req.Email != "" && !subimporter.IsEmail(req.Email) {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("subscribers.invalidEmail"))
if em, err := app.importer.SanitizeEmail(req.Email); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} else {
req.Email = em
}
if req.Name != "" && !strHasLen(req.Name, 1, stdInputMaxLen) {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("subscribers.invalidName"))
}

View file

@ -0,0 +1,5 @@
email,name,attributes
noban1-import@mail.com,First0 Last0,"{""age"": 29, ""city"": ""Bangalore"", ""clientId"": ""DAXX79""}"
ban1-import@BAN.net,First1 Last1,"{""age"": 43, ""city"": ""Bangalore"", ""clientId"": ""DAXX71""}"
noban2-import1@mail.com,First2 Last2,"{""age"": 47, ""city"": ""Bangalore"", ""clientId"": ""DAXX70""}"
ban2-import@ban.ORG,First1 Last1,"{""age"": 43, ""city"": ""Bangalore"", ""clientId"": ""DAXX71""}"
1 email name attributes
2 noban1-import@mail.com First0 Last0 {"age": 29, "city": "Bangalore", "clientId": "DAXX79"}
3 ban1-import@BAN.net First1 Last1 {"age": 43, "city": "Bangalore", "clientId": "DAXX71"}
4 noban2-import1@mail.com First2 Last2 {"age": 47, "city": "Bangalore", "clientId": "DAXX70"}
5 ban2-import@ban.ORG First1 Last1 {"age": 43, "city": "Bangalore", "clientId": "DAXX71"}

View file

@ -1,3 +1,5 @@
const apiUrl = Cypress.env('apiUrl');
describe('Subscribers', () => {
it('Opens subscribers page', () => {
cy.resetDB();
@ -43,6 +45,7 @@ describe('Subscribers', () => {
});
cy.get('[data-cy=btn-query-reset]').click();
cy.wait(1000);
cy.get('tbody td[data-label=Status]').its('length').should('eq', 2);
});
@ -55,7 +58,7 @@ describe('Subscribers', () => {
{ radio: 'check-list-remove', lists: [0, 1], rows: { 1: [] } },
{ radio: 'check-list-add', lists: [0, 1], rows: { 0: ['unsubscribed', 'unsubscribed'], 1: ['unconfirmed', 'unconfirmed'] } },
{ radio: 'check-list-remove', lists: [0], rows: { 0: ['unsubscribed'] } },
{ radio: 'check-list-add', lists: [0], rows: { 0: ['unconfirmed', 'unsubscribed'] } },
{ radio: 'check-list-add', lists: [0], rows: { 0: ['unsubscribed', 'unconfirmed'] } },
];
@ -109,7 +112,7 @@ describe('Subscribers', () => {
// Open the edit popup and edit the default lists.
cy.get('[data-cy=btn-edit]').each(($el, n) => {
const email = `email-${n}@email.com`;
const email = `email-${n}@EMAIL.com`;
const name = `name-${n}`;
// Open the edit modal.
@ -136,7 +139,7 @@ describe('Subscribers', () => {
cy.wait(250);
cy.get('tbody tr').each(($el) => {
cy.wrap($el).find('td[data-id]').invoke('attr', 'data-id').then((id) => {
cy.wrap($el).find('td[data-label=E-mail]').contains(rows[id].email);
cy.wrap($el).find('td[data-label=E-mail]').contains(rows[id].email.toLowerCase());
cy.wrap($el).find('td[data-label=Name]').contains(rows[id].name);
cy.wrap($el).find('td[data-label=Status]').contains(rows[id].status, { matchCase: false });
@ -171,7 +174,7 @@ describe('Subscribers', () => {
// Cycle through each status and each list ID combination and create subscribers.
const n = 0;
for (let n = 0; n < 6; n++) {
const email = `email-${n}@email.com`;
const email = `email-${n}@EMAIL.com`;
const name = `name-${n}`;
const status = statuses[(n + 1) % statuses.length];
const list = lists[(n + 1) % lists.length];
@ -192,7 +195,7 @@ describe('Subscribers', () => {
// which is always the first row in the table.
cy.wait(250);
const tr = cy.get('tbody tr:nth-child(1)').then(($el) => {
cy.wrap($el).find('td[data-label=E-mail]').contains(email);
cy.wrap($el).find('td[data-label=E-mail]').contains(email.toLowerCase());
cy.wrap($el).find('td[data-label=Name]').contains(name);
cy.wrap($el).find('td[data-label=Status]').contains(status, { matchCase: false });
cy.wrap($el).find(`.tags .${status === 'enabled' ? 'unconfirmed' : 'unsubscribed'}`)
@ -217,3 +220,104 @@ describe('Subscribers', () => {
});
});
});
describe('Domain blocklist', () => {
it('Opens settings page', () => {
cy.resetDB();
});
it('Add domains to blocklist', () => {
cy.loginAndVisit('/settings');
cy.get('.b-tabs nav a').eq(2).click();
cy.get('textarea[name="privacy.domain_blocklist"]').clear().type('ban.net\n\nBaN.OrG\n\nban.com\n\n');
cy.get('[data-cy=btn-save]').click();
});
it('Try subscribing via public page', () => {
cy.visit(`${apiUrl}/subscription/form`);
cy.get('input[name=email]').clear().type('test@noban.net');
cy.get('button[type=submit]').click();
cy.get('h2').contains('Subscribe');
cy.visit(`${apiUrl}/subscription/form`);
cy.get('input[name=email]').clear().type('test@ban.net');
cy.get('button[type=submit]').click();
cy.get('h2').contains('Error');
});
// Post to the admin API.
it('Try via admin API', () => {
cy.wait(1000);
// Add non-banned domain.
cy.request({
method: 'POST', url: `${apiUrl}/api/subscribers`, failOnStatusCode: true,
body: { email: 'test1@noban.net', 'name': 'test', 'lists': [1], 'status': 'enabled' }
}).should((response) => {
expect(response.status).to.equal(200);
});
// Add banned domain.
cy.request({
method: 'POST', url: `${apiUrl}/api/subscribers`, failOnStatusCode: false,
body: { email: 'test1@ban.com', 'name': 'test', 'lists': [1], 'status': 'enabled' }
}).should((response) => {
expect(response.status).to.equal(400);
});
// Modify an existinb subscriber to a banned domain.
cy.request({
method: 'PUT', url: `${apiUrl}/api/subscribers/1`, failOnStatusCode: false,
body: { email: 'test3@ban.org', 'name': 'test', 'lists': [1], 'status': 'enabled' }
}).should((response) => {
expect(response.status).to.equal(400);
});
});
it('Try via import', () => {
cy.loginAndVisit('/subscribers/import');
cy.get('.list-selector input').click();
cy.get('.list-selector .autocomplete a').first().click();
cy.fixture('subs-domain-blocklist.csv').then((data) => {
cy.get('input[type="file"]').attachFile({
fileContent: data.toString(),
fileName: 'subs.csv',
mimeType: 'text/csv',
});
});
cy.get('button.is-primary').click();
cy.get('section.wrap .has-text-success');
// cy.get('button.is-primary').click();
cy.get('.log-view').should('contain', 'ban1-import@BAN.net').and('contain', 'ban2-import@ban.ORG');
cy.wait(100);
});
it('Clear blocklist and try', () => {
cy.loginAndVisit('/settings');
cy.get('.b-tabs nav a').eq(2).click();
cy.get('textarea[name="privacy.domain_blocklist"]').clear();
cy.get('[data-cy=btn-save]').click();
cy.wait(1000);
// Add banned domain.
cy.request({
method: 'POST', url: `${apiUrl}/api/subscribers`, failOnStatusCode: true,
body: { email: 'test4@BAN.com', 'name': 'test', 'lists': [1], 'status': 'enabled' }
}).should((response) => {
expect(response.status).to.equal(200);
});
// Modify an existinb subscriber to a banned domain.
cy.request({
method: 'PUT', url: `${apiUrl}/api/subscribers/1`, failOnStatusCode: true,
body: { email: 'test4@BAN.org', 'name': 'test', 'lists': [1], 'status': 'enabled' }
}).should((response) => {
expect(response.status).to.equal(200);
});
});
})

View file

@ -35,7 +35,7 @@
"@vue/cli-service": "~4.5.13",
"@vue/eslint-config-airbnb": "^5.3.0",
"babel-eslint": "^10.1.0",
"cypress": "^6.4.0",
"cypress": "8.4.1",
"cypress-file-upload": "^5.0.2",
"eslint": "^7.27.0",
"eslint-plugin-import": "^2.23.3",

View file

@ -134,6 +134,9 @@ export default Vue.extend({
}
}
// Domain blocklist array from multi-line strings.
form['privacy.domain_blocklist'] = form['privacy.domain_blocklist'].split('\n').map((v) => v.trim().toLowerCase()).filter((v) => v !== '');
this.isLoading = true;
this.$api.updateSettings(form).then((data) => {
if (data.needsRestart) {
@ -192,6 +195,9 @@ export default Vue.extend({
}
d['bounce.sendgrid_key'] = dummyPassword;
// Domain blocklist array to multi-line string.
d['privacy.domain_blocklist'] = d['privacy.domain_blocklist'].join('\n');
this.key += 1;
this.form = d;
this.formCopy = JSON.stringify(d);

View file

@ -18,84 +18,86 @@
</div>
</header>
<section class="subscribers-controls columns">
<div class="column is-4">
<form @submit.prevent="onSubmit">
<div>
<b-field grouped>
<b-input @input="onSimpleQueryInput" v-model="queryInput"
:placeholder="$t('subscribers.queryPlaceholder')" icon="magnify" ref="query"
:disabled="isSearchAdvanced" data-cy="search"></b-input>
<b-button native-type="submit" type="is-primary" icon-left="magnify"
:disabled="isSearchAdvanced" data-cy="btn-search"></b-button>
</b-field>
<section class="subscribers-controls">
<div class="columns">
<div class="column is-6">
<form @submit.prevent="onSubmit">
<div>
<b-field grouped>
<b-input @input="onSimpleQueryInput" v-model="queryInput"
:placeholder="$t('subscribers.queryPlaceholder')" icon="magnify" ref="query"
:disabled="isSearchAdvanced" data-cy="search"></b-input>
<b-button native-type="submit" type="is-primary" icon-left="magnify"
:disabled="isSearchAdvanced" data-cy="btn-search"></b-button>
</b-field>
<p>
<a href="#" @click.prevent="toggleAdvancedSearch" data-cy="btn-advanced-search">
<b-icon icon="cog-outline" size="is-small" />
{{ $t('subscribers.advancedQuery') }}
</a>
</p>
<div v-if="isSearchAdvanced">
<b-field>
<b-input v-model="queryParams.queryExp"
@keydown.native.enter="onAdvancedQueryEnter"
type="textarea" ref="queryExp"
placeholder="subscribers.name LIKE '%user%' or subscribers.status='blocklisted'"
data-cy="query">
</b-input>
</b-field>
<b-field>
<span class="is-size-6 has-text-grey">
{{ $t('subscribers.advancedQueryHelp') }}.{{ ' ' }}
<a href="https://listmonk.app/docs/querying-and-segmentation"
target="_blank" rel="noopener noreferrer">
{{ $t('globals.buttons.learnMore') }}.
</a>
</span>
</b-field>
<div class="buttons">
<b-button native-type="submit" type="is-primary"
icon-left="magnify" data-cy="btn-query">{{ $t('subscribers.query') }}</b-button>
<b-button @click.prevent="toggleAdvancedSearch" icon-left="cancel"
data-cy="btn-query-reset">
{{ $t('subscribers.reset') }}
</b-button>
</div>
</div><!-- advanced query -->
</div>
</form>
</div><!-- search -->
<div class="column is-6 subscribers-bulk" v-if="bulk.checked.length > 0">
<div>
<p>
<a href="#" @click.prevent="toggleAdvancedSearch" data-cy="btn-advanced-search">
<b-icon icon="cog-outline" size="is-small" />
{{ $t('subscribers.advancedQuery') }}
</a>
<span class="is-size-5 has-text-weight-semibold">
{{ $t('subscribers.numSelected', { num: numSelectedSubscribers }) }}
</span>
<span v-if="!bulk.all && subscribers.total > subscribers.perPage">
&mdash;
<a href="" @click.prevent="selectAllSubscribers">
{{ $t('subscribers.selectAll', { num: subscribers.total }) }}
</a>
</span>
</p>
<div v-if="isSearchAdvanced">
<b-field>
<b-input v-model="queryParams.queryExp"
@keydown.native.enter="onAdvancedQueryEnter"
type="textarea" ref="queryExp"
placeholder="subscribers.name LIKE '%user%' or subscribers.status='blocklisted'"
data-cy="query">
</b-input>
</b-field>
<b-field>
<span class="is-size-6 has-text-grey">
{{ $t('subscribers.advancedQueryHelp') }}.{{ ' ' }}
<a href="https://listmonk.app/docs/querying-and-segmentation"
target="_blank" rel="noopener noreferrer">
{{ $t('globals.buttons.learnMore') }}.
</a>
</span>
</b-field>
<div class="buttons">
<b-button native-type="submit" type="is-primary"
icon-left="magnify" data-cy="btn-query">{{ $t('subscribers.query') }}</b-button>
<b-button @click.prevent="toggleAdvancedSearch" icon-left="cancel"
data-cy="btn-query-reset">
{{ $t('subscribers.reset') }}
</b-button>
</div>
</div><!-- advanced query -->
</div>
</form>
</div><!-- search -->
<div class="column is-4 subscribers-bulk" v-if="bulk.checked.length > 0">
<div>
<p>
<span class="is-size-5 has-text-weight-semibold">
{{ $t('subscribers.numSelected', { num: numSelectedSubscribers }) }}
</span>
<span v-if="!bulk.all && subscribers.total > subscribers.perPage">
&mdash;
<a href="" @click.prevent="selectAllSubscribers">
{{ $t('subscribers.selectAll', { num: subscribers.total }) }}
<p class="actions">
<a href='' @click.prevent="showBulkListForm" data-cy="btn-manage-lists">
<b-icon icon="format-list-bulleted-square" size="is-small" /> Manage lists
</a>
</span>
</p>
<p class="actions">
<a href='' @click.prevent="showBulkListForm" data-cy="btn-manage-lists">
<b-icon icon="format-list-bulleted-square" size="is-small" /> Manage lists
</a>
<a href='' @click.prevent="deleteSubscribers" data-cy="btn-delete-subscribers">
<b-icon icon="trash-can-outline" size="is-small" /> Delete
</a>
<a href='' @click.prevent="deleteSubscribers" data-cy="btn-delete-subscribers">
<b-icon icon="trash-can-outline" size="is-small" /> Delete
</a>
<a href='' @click.prevent="blocklistSubscribers" data-cy="btn-manage-blocklist">
<b-icon icon="account-off-outline" size="is-small" /> Blocklist
</a>
</p><!-- selection actions //-->
<a href='' @click.prevent="blocklistSubscribers" data-cy="btn-manage-blocklist">
<b-icon icon="account-off-outline" size="is-small" /> Blocklist
</a>
</p><!-- selection actions //-->
</div>
</div>
</div>
</section><!-- control -->

View file

@ -29,6 +29,13 @@
<b-switch v-model="data['privacy.allow_wipe']"
name="privacy.allow_wipe" />
</b-field>
<b-field :label="$t('settings.privacy.domainBlocklist')"
:message="$t('settings.privacy.domainBlocklistHelp')">
<b-input type="textarea"
v-model="data['privacy.domain_blocklist']"
name="privacy.domain_blocklist" />
</b-field>
</div>
</template>

539
frontend/yarn.lock vendored
View file

@ -1131,20 +1131,10 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@cypress/listr-verbose-renderer@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a"
integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=
dependencies:
chalk "^1.1.3"
cli-cursor "^1.0.2"
date-fns "^1.27.2"
figures "^1.7.0"
"@cypress/request@^2.88.5":
version "2.88.5"
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7"
integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==
"@cypress/request@^2.88.6":
version "2.88.6"
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.6.tgz#a970dd675befc6bdf8a8921576c01f51cc5798e9"
integrity sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
@ -1159,13 +1149,12 @@
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.5.0"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
uuid "^8.3.2"
"@cypress/xvfb@^1.2.4":
version "1.2.4"
@ -1244,13 +1233,6 @@
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301"
integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==
dependencies:
any-observable "^0.3.0"
"@soda/friendly-errors-webpack-plugin@^1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz#706f64bcb4a8b9642b48ae3ace444c70334d615d"
@ -1367,10 +1349,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.11.tgz#61d4886e2424da73b7b25547f59fdcb534c165a3"
integrity sha512-lCvvI24L21ZVeIiyIUHZ5Oflv1hhHQ5E1S25IRlKIXaRkVgmXpJMI3wUJkmym2bTbCe+WoIibQnMVAU3FguaOg==
"@types/node@12.12.50":
version "12.12.50"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.50.tgz#e9b2e85fafc15f2a8aa8fdd41091b983da5fd6ee"
integrity sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==
"@types/node@^14.14.31":
version "14.17.18"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.18.tgz#0198489a751005f71217744aa966cd1f29447c81"
integrity sha512-haYyibw4pbteEhkSg0xdDLAI3679L75EJ799ymVrPxOA922bPx3ML59SoDsQ//rHlvqpu+e36kcbR3XRQtFblA==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@ -1405,10 +1387,10 @@
"@types/mime" "^1"
"@types/node" "*"
"@types/sinonjs__fake-timers@^6.0.1":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae"
integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==
"@types/sinonjs__fake-timers@^6.0.2":
version "6.0.4"
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d"
integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==
"@types/sizzle@^2.3.2":
version "2.3.2"
@ -1464,6 +1446,13 @@
anymatch "^3.0.0"
source-map "^0.6.0"
"@types/yauzl@^2.9.1":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a"
integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==
dependencies:
"@types/node" "*"
"@vue/babel-helper-vue-jsx-merge-props@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz#31624a7a505fb14da1d58023725a4c5f270e6a81"
@ -1943,6 +1932,14 @@ address@^1.1.2:
resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
dependencies:
clean-stack "^2.0.0"
indent-string "^4.0.0"
ajv-errors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
@ -2003,11 +2000,6 @@ ansi-colors@^4.1.1:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
ansi-escapes@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
ansi-escapes@^4.2.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
@ -2015,6 +2007,13 @@ ansi-escapes@^4.2.1:
dependencies:
type-fest "^0.11.0"
ansi-escapes@^4.3.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
dependencies:
type-fest "^0.21.3"
ansi-html@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
@ -2060,11 +2059,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"
any-observable@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
@ -2104,7 +2098,7 @@ arch@^2.1.1:
resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf"
integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==
arch@^2.1.2:
arch@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
@ -2415,7 +2409,7 @@ bindings@^1.5.0:
dependencies:
file-uri-to-path "1.0.0"
blob-util@2.0.2:
blob-util@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb"
integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==
@ -2777,7 +2771,7 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
chalk@^1.0.0, chalk@^1.1.3:
chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
@ -2894,10 +2888,10 @@ ci-info@^1.5.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==
ci-info@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
ci-info@^3.1.1:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6"
integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
@ -2924,14 +2918,12 @@ clean-css@4.2.x:
dependencies:
source-map "~0.6.0"
cli-cursor@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=
dependencies:
restore-cursor "^1.0.1"
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
cli-cursor@^2.0.0, cli-cursor@^2.1.0:
cli-cursor@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
@ -2972,13 +2964,13 @@ cli-table3@~0.6.0:
optionalDependencies:
colors "^1.1.2"
cli-truncate@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=
cli-truncate@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
dependencies:
slice-ansi "0.0.4"
string-width "^1.0.1"
slice-ansi "^3.0.0"
string-width "^4.2.0"
cli-width@^2.0.0:
version "2.2.1"
@ -3026,11 +3018,6 @@ coa@^2.0.2:
chalk "^2.4.1"
q "^1.1.2"
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codeflask@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/codeflask/-/codeflask-1.4.1.tgz#c5229854e3f648377922a75f1145f7316030d3db"
@ -3092,6 +3079,11 @@ colorette@^1.2.1, colorette@^1.2.2:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
colorette@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
colors@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
@ -3164,7 +3156,7 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
concat-stream@^1.5.0, concat-stream@^1.6.2:
concat-stream@^1.5.0:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
@ -3558,47 +3550,48 @@ cypress-file-upload@^5.0.2:
dependencies:
mime "^2.5.0"
cypress@^6.4.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.8.0.tgz#8338f39212a8f71e91ff8c017a1b6e22d823d8c1"
integrity sha512-W2e9Oqi7DmF48QtOD0LfsOLVq6ef2hcXZvJXI/E3PgFNmZXEVwBefhAxVCW9yTPortjYA2XkM20KyC4HRkOm9w==
cypress@8.4.1:
version "8.4.1"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.4.1.tgz#8b5898bf49359cadc28f02ba05d51f63b8e3a717"
integrity sha512-itJXq0Vx3sXCUrDyBi2IUrkxVu/gTTp1VhjB5tzGgkeCR8Ae+/T8WV63rsZ7fS8Tpq7LPPXiyoM/sEdOX7cR6A==
dependencies:
"@cypress/listr-verbose-renderer" "^0.4.1"
"@cypress/request" "^2.88.5"
"@cypress/request" "^2.88.6"
"@cypress/xvfb" "^1.2.4"
"@types/node" "12.12.50"
"@types/sinonjs__fake-timers" "^6.0.1"
"@types/node" "^14.14.31"
"@types/sinonjs__fake-timers" "^6.0.2"
"@types/sizzle" "^2.3.2"
arch "^2.1.2"
blob-util "2.0.2"
arch "^2.2.0"
blob-util "^2.0.2"
bluebird "^3.7.2"
cachedir "^2.3.0"
chalk "^4.1.0"
check-more-types "^2.24.0"
cli-cursor "^3.1.0"
cli-table3 "~0.6.0"
commander "^5.1.0"
common-tags "^1.8.0"
dayjs "^1.9.3"
debug "4.3.2"
eventemitter2 "^6.4.2"
execa "^4.0.2"
dayjs "^1.10.4"
debug "^4.3.2"
enquirer "^2.3.6"
eventemitter2 "^6.4.3"
execa "4.1.0"
executable "^4.1.1"
extract-zip "^1.7.0"
fs-extra "^9.0.1"
extract-zip "2.0.1"
figures "^3.2.0"
fs-extra "^9.1.0"
getos "^3.2.1"
is-ci "^2.0.0"
is-installed-globally "^0.3.2"
is-ci "^3.0.0"
is-installed-globally "~0.4.0"
lazy-ass "^1.6.0"
listr "^0.14.3"
lodash "^4.17.19"
listr2 "^3.8.3"
lodash "^4.17.21"
log-symbols "^4.0.0"
minimist "^1.2.5"
moment "^2.29.1"
ospath "^1.2.2"
pretty-bytes "^5.4.1"
pretty-bytes "^5.6.0"
ramda "~0.27.1"
request-progress "^3.0.0"
supports-color "^7.2.0"
supports-color "^8.1.1"
tmp "~0.2.1"
untildify "^4.0.0"
url "^0.11.0"
@ -3859,12 +3852,7 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
date-fns@^1.27.2:
version "1.30.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
dayjs@^1.10.4, dayjs@^1.9.3:
dayjs@^1.10.4:
version "1.10.4"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2"
integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==
@ -3881,13 +3869,6 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
debug@^3.1.0, debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@ -3909,6 +3890,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "^2.1.1"
debug@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@ -4201,11 +4189,6 @@ electron-to-chromium@^1.3.723:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09"
integrity sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==
elegant-spinner@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
elliptic@^6.0.0, elliptic@^6.5.2:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
@ -4269,7 +4252,7 @@ enhanced-resolve@^4.1.0:
memory-fs "^0.5.0"
tapable "^1.0.0"
enquirer@^2.3.5:
enquirer@^2.3.5, enquirer@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
@ -4624,7 +4607,7 @@ event-pubsub@4.3.0:
resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e"
integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==
eventemitter2@^6.4.2:
eventemitter2@^6.4.3:
version "6.4.4"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.4.tgz#aa96e8275c4dbeb017a5d0e03780c65612a1202b"
integrity sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw==
@ -4654,6 +4637,21 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
md5.js "^1.3.4"
safe-buffer "^5.1.1"
execa@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
dependencies:
cross-spawn "^7.0.0"
get-stream "^5.0.0"
human-signals "^1.1.1"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.0"
onetime "^5.1.0"
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
execa@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
@ -4696,21 +4694,6 @@ execa@^3.3.0:
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
execa@^4.0.2:
version "4.1.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
dependencies:
cross-spawn "^7.0.0"
get-stream "^5.0.0"
human-signals "^1.1.1"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.0"
onetime "^5.1.0"
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
executable@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
@ -4718,11 +4701,6 @@ executable@^4.1.1:
dependencies:
pify "^2.2.0"
exit-hook@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@ -4815,15 +4793,16 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-zip@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
extract-zip@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
dependencies:
concat-stream "^1.6.2"
debug "^2.6.9"
mkdirp "^0.5.4"
debug "^4.1.1"
get-stream "^5.1.0"
yauzl "^2.10.0"
optionalDependencies:
"@types/yauzl" "^2.9.1"
extsprintf@1.3.0:
version "1.3.0"
@ -4893,22 +4872,7 @@ figgy-pudding@^3.5.1:
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
figures@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=
dependencies:
escape-string-regexp "^1.0.5"
object-assign "^4.1.0"
figures@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
dependencies:
escape-string-regexp "^1.0.5"
figures@^3.0.0:
figures@^3.0.0, figures@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
@ -5111,7 +5075,7 @@ fs-extra@^7.0.1:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^9.0.1:
fs-extra@^9.1.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
@ -5202,6 +5166,13 @@ get-stream@^5.0.0:
dependencies:
pump "^3.0.0"
get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@ -5253,12 +5224,12 @@ glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d"
integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==
global-dirs@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686"
integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==
dependencies:
ini "1.3.7"
ini "2.0.0"
globals@^11.1.0:
version "11.12.0"
@ -5742,10 +5713,10 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
indent-string@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
indent.js@^0.3.5:
version "0.3.5"
@ -5785,10 +5756,10 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
ini@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
inquirer@^7.1.0:
version "7.1.0"
@ -5931,12 +5902,12 @@ is-ci@^1.0.10:
dependencies:
ci-info "^1.5.0"
is-ci@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
is-ci@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994"
integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==
dependencies:
ci-info "^2.0.0"
ci-info "^3.1.1"
is-color-stop@^1.0.0:
version "1.1.0"
@ -6021,13 +5992,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
is-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
@ -6052,13 +6016,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
dependencies:
is-extglob "^2.1.1"
is-installed-globally@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==
is-installed-globally@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==
dependencies:
global-dirs "^2.0.1"
is-path-inside "^3.0.1"
global-dirs "^3.0.0"
is-path-inside "^3.0.2"
is-negative-zero@^2.0.1:
version "2.0.1"
@ -6087,13 +6051,6 @@ is-obj@^2.0.0:
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
is-observable@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e"
integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==
dependencies:
symbol-observable "^1.1.0"
is-path-cwd@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
@ -6113,7 +6070,7 @@ is-path-inside@^2.1.0:
dependencies:
path-is-inside "^1.0.2"
is-path-inside@^3.0.1:
is-path-inside@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
@ -6135,11 +6092,6 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
is-promise@^2.1.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
is-regex@^1.0.4, is-regex@^1.0.5:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff"
@ -6454,49 +6406,18 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
listr-silent-renderer@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=
listr-update-renderer@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2"
integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==
listr2@^3.8.3:
version "3.12.2"
resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.12.2.tgz#2d55cc627111603ad4768a9e87c9c7bb9b49997e"
integrity sha512-64xC2CJ/As/xgVI3wbhlPWVPx0wfTqbUAkpb7bjDi0thSWMqrf07UFhrfsGoo8YSXmF049Rp9C0cjLC8rZxK9A==
dependencies:
chalk "^1.1.3"
cli-truncate "^0.2.1"
elegant-spinner "^1.0.1"
figures "^1.7.0"
indent-string "^3.0.0"
log-symbols "^1.0.2"
log-update "^2.3.0"
strip-ansi "^3.0.1"
listr-verbose-renderer@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db"
integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==
dependencies:
chalk "^2.4.1"
cli-cursor "^2.1.0"
date-fns "^1.27.2"
figures "^2.0.0"
listr@^0.14.3:
version "0.14.3"
resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586"
integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==
dependencies:
"@samverschueren/stream-to-observable" "^0.3.0"
is-observable "^1.1.0"
is-promise "^2.1.0"
is-stream "^1.1.0"
listr-silent-renderer "^1.1.1"
listr-update-renderer "^0.5.0"
listr-verbose-renderer "^0.5.0"
p-map "^2.0.0"
rxjs "^6.3.3"
cli-truncate "^2.1.0"
colorette "^1.4.0"
log-update "^4.0.0"
p-map "^4.0.0"
rxjs "^6.6.7"
through "^2.3.8"
wrap-ansi "^7.0.0"
load-json-file@^4.0.0:
version "4.0.0"
@ -6627,18 +6548,11 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.3:
lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.3:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=
dependencies:
chalk "^1.0.0"
log-symbols@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
@ -6654,14 +6568,15 @@ log-symbols@^4.0.0:
chalk "^4.1.0"
is-unicode-supported "^0.1.0"
log-update@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708"
integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg=
log-update@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
dependencies:
ansi-escapes "^3.0.0"
cli-cursor "^2.0.0"
wrap-ansi "^3.0.1"
ansi-escapes "^4.3.0"
cli-cursor "^3.1.0"
slice-ansi "^4.0.0"
wrap-ansi "^6.2.0"
loglevel@^1.6.8:
version "1.6.8"
@ -6936,18 +6851,13 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.1:
mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
moment@^2.29.1:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -7177,11 +7087,6 @@ num2fraction@^1.2.2:
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
@ -7324,11 +7229,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
onetime@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=
onetime@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
@ -7458,6 +7358,13 @@ p-map@^2.0.0:
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
p-map@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
dependencies:
aggregate-error "^3.0.0"
p-retry@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328"
@ -8083,7 +7990,7 @@ prettier@^1.18.2:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
pretty-bytes@^5.4.1:
pretty-bytes@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
@ -8556,14 +8463,6 @@ resolve@^1.14.2, resolve@^1.20.0:
is-core-module "^2.2.0"
path-parse "^1.0.6"
restore-cursor@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=
dependencies:
exit-hook "^1.0.0"
onetime "^1.0.0"
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@ -8639,13 +8538,6 @@ rw@1:
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
rxjs@^6.3.3:
version "6.6.6"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70"
integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==
dependencies:
tslib "^1.9.0"
rxjs@^6.5.3:
version "6.5.5"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
@ -8653,6 +8545,13 @@ rxjs@^6.5.3:
dependencies:
tslib "^1.9.0"
rxjs@^6.6.7:
version "6.6.7"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
dependencies:
tslib "^1.9.0"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@ -8918,10 +8817,14 @@ slash@^2.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=
slice-ansi@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
dependencies:
ansi-styles "^4.0.0"
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
slice-ansi@^4.0.0:
version "4.0.0"
@ -9184,16 +9087,7 @@ strict-uri-encode@^1.0.0:
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
dependencies:
code-point-at "^1.0.0"
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
string-width@^2.0.0, string-width@^2.1.1:
string-width@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@ -9364,13 +9258,20 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"
supports-color@^7.1.0, supports-color@^7.2.0:
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-color@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
svg-tags@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
@ -9395,11 +9296,6 @@ svgo@^1.0.0:
unquote "~1.1.1"
util.promisify "~1.0.0"
symbol-observable@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
table@^6.0.9:
version "6.7.1"
resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2"
@ -9507,7 +9403,7 @@ through2@^2.0.0:
readable-stream "~2.3.6"
xtend "~4.0.1"
through@^2.3.6:
through@^2.3.6, through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@ -9674,6 +9570,11 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^0.21.3:
version "0.21.3"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
type-fest@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
@ -9909,6 +9810,11 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache@^2.0.3:
version "2.1.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"
@ -10248,14 +10154,6 @@ worker-farm@^1.7.0:
dependencies:
errno "~0.1.7"
wrap-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba"
integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=
dependencies:
string-width "^2.1.1"
strip-ansi "^4.0.0"
wrap-ansi@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
@ -10274,6 +10172,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Umožnit odběratelům exportovat shromážděná data?",
"settings.privacy.allowWipe": "Umožnit vymazání",
"settings.privacy.allowWipeHelp": "Umožnit odběratelům odstranit sebe včetně svých odběrů a všech ostatních dat z databáze. Pohledy na kampaně a klepnutí na odkazy se rovněž odeberou, zatímco pohledy a počty klepnutí se zachovají (aniž by měly přidruženého odběratele), takže statistiky a analýzy nebudou ovlivněny.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Sledování jednotlivých odběratelů",
"settings.privacy.individualSubTrackingHelp": "Sledovat klepnutí a pohledy na kampaně na úrovni odběratelů. Je-li to zakázáno, sledování klepnutí a pohledů pokračuje, aniž by bylo propojeno s jednotlivými odběrateli.",
"settings.privacy.listUnsubHeader": "Zahrnout záhlaví `List-Unsubscribe`",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Blokovat {num} odběratelů?",
"subscribers.confirmDelete": "Odstranit {num} odběratelů?",
"subscribers.confirmExport": "Exportovat {num} odběratelů?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Stáhnout data",
"subscribers.email": "E-mail",
"subscribers.emailExists": "E-mail již existuje.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Erlaube Abonnenten alle ihre Daten zu exportieren?",
"settings.privacy.allowWipe": "Löschen aktivieren",
"settings.privacy.allowWipeHelp": "Erlaube Abonnenten alle Daten, welche über sie gespeichert sind zu löschen. Dies beinhaltet auch Klicks und Anzeigen, verändert allerdings nicht die Gesamtzahl. Statistiken bleiben auch unverändert.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Einzelabonnenten Tracking",
"settings.privacy.individualSubTrackingHelp": "Abonnentenviews und Klicks werden einzeln getrackt. Wenn deaktiviert, werden die Daten ohne Zuordnung zu Abonnenten gespeichert.",
"settings.privacy.listUnsubHeader": "Inkludiere `List-Unsubscribe` (von Liste abmelden) Header",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Blockiere {num} Abonnent(en)?",
"subscribers.confirmDelete": "Lösche {num} Abonnent(en)?",
"subscribers.confirmExport": "Exportiere {num} Abonnent(en)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Daten herunterladen",
"subscribers.email": "E-Mail",
"subscribers.emailExists": "E-Mail existiert bereits.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Allow subscribers to export data collected on them?",
"settings.privacy.allowWipe": "Allow wiping",
"settings.privacy.allowWipeHelp": "Allow subscribers to delete themselves including their subscriptions and all other data from the database. Campaign views and link clicks are also removed while views and click counts remain (with no subscriber associated to them) so that stats and analytics are not affected.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Individual subscriber tracking",
"settings.privacy.individualSubTrackingHelp": "Track subscriber-level campaign views and clicks. When disabled, view and click tracking continue without being linked to individual subscribers.",
"settings.privacy.listUnsubHeader": "Include `List-Unsubscribe` header",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Blocklist {num} subscriber(s)?",
"subscribers.confirmDelete": "Delete {num} subscriber(s)?",
"subscribers.confirmExport": "Export {num} subscriber(s)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Download data",
"subscribers.email": "E-mail",
"subscribers.emailExists": "E-mail already exists.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "¿Permitir a los subscriptores exportar los datos recabados de ellos?",
"settings.privacy.allowWipe": "Permitir limpieza de datos",
"settings.privacy.allowWipeHelp": "Permitir a los subscriptores eliminarse incluyendo sus subscripciones y todos sus datos de la base de datos. Las vistas de las campañas y los vínculos cliqueados también son eliminados mientras que las vistas y el conteo de clics se mantienen. (sin subscriptores asociados a ellos) de manera que las estadísticas y el análisis no se vea afectado.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Seguimiento de subscriptor inválido.",
"settings.privacy.individualSubTrackingHelp": "Seguir a nivel de subscriptor las vistas y clics en una campaña. Cuando está deshabilitado, el seguimiento de vistas y clics continua sin ser asociado con subscriptores individuales.",
"settings.privacy.listUnsubHeader": "Incluir el encabezado `Des-subscribirse` de la lista",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Blocklist {num} subscriptor(es)?",
"subscribers.confirmDelete": "Borrar {num} subscriptor(es)?",
"subscribers.confirmExport": "Exportar {num} subscriptor(es)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Descargar datos",
"subscribers.email": "Correo electrónico",
"subscribers.emailExists": "El correo electrónico ya existe.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Autoriser les abonné·es à exporter les données collectées à leur sujet ?",
"settings.privacy.allowWipe": "Autoriser la suppression des données par les abonné·es",
"settings.privacy.allowWipeHelp": "Autoriser les abonné·es à supprimer leurs abonnements et toutes les autres données de la base de données. Les vues de campagne et les clics sur les liens sont également supprimés, tandis que le compteur de vues et de nombre de clics globaux restent inchangés (aucun·e abonné·e ne leur est associé) afin que les statistiques et les analyses ne soient pas affectées.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Suivi individuel des abonné·es (vérifiez si la légalislation l'autorise)",
"settings.privacy.individualSubTrackingHelp": "Suivez les vues et les clics par abonné·e pour les campagnes (vérifiez si la légalislation en vigueur l'autorise). Si l'option est désactivée, le suivi des vues et des clics s'effectue de façon anonyme.",
"settings.privacy.listUnsubHeader": "Inclure l'en-tête de désabonnement simplifié (via certaines messageries)",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Bloquer {num} abonné·e(s) ?",
"subscribers.confirmDelete": "Supprimer {num} abonné·e(s) ?",
"subscribers.confirmExport": "Exporter {num} abonné·e(s) ?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Télécharger les données",
"subscribers.email": "Email",
"subscribers.emailExists": "Cet email existe déjà.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Autorizzi gli iscritti a esportare i dati raccolti su di loro?",
"settings.privacy.allowWipe": "Autorizza la cancellazione",
"settings.privacy.allowWipeHelp": "Autorizza gli iscritti a cancellare le loro iscrizioni e tutti gli altri dati dal database. Le visualizzazioni della campagna e i clic sui link verranno anch'essi cancellati, mentre i contatori globali delle visualizzazioni e del numero di clic restano invariati (nessun iscritto vi è associato) in modo che le statistiche non siano compromesse.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Follow-up individuale degli abbonati",
"settings.privacy.individualSubTrackingHelp": "Monitora le visualizzazioni e i clic della campagna per iscritto. Quando è disabilitato, il follow-up delle visualizzazioni e dei clic, si effettua senza essere legato agli iscritti individuali.",
"settings.privacy.listUnsubHeader": "Includere l'intestazione `List-Unsubscribe`",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Lista di blocco {num} iscritto(i)?",
"subscribers.confirmDelete": "Elimina {num} iscrittoi(i)?",
"subscribers.confirmExport": "Esporta {num} iscritto(i)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Scarica i dati",
"subscribers.email": "Email",
"subscribers.emailExists": "Email già esistente.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "ഉപഭോക്കാക്കളിൽ നിന്നും ശേഖരിച്ച വിവരങ്ങൾ എക്സ്പോർട്ട് ചെയ്യാൻ അനുവദിക്കണോ?",
"settings.privacy.allowWipe": "വിവരങ്ങൾ എന്നന്നേയ്ക്കുമായി ഇല്ലാതാക്കുന്നത് അനുവദിക്കുക",
"settings.privacy.allowWipeHelp": "ഉപഭോക്താക്കളെ അവരുടെ വരിക്കാരായിട്ടുള്ള ലിസ്റ്റുകളും മറ്റു വിവരങ്ങളും ഡാറ്റാബേസിൽ നിന്നും ഇല്ലാതാക്കാൻ അനുവദിക്കുക.ക്യാമ്പെയ്ൻ കാഴ്ചകളും കണ്ണികളിന്മേലുള്ള ക്ലിക്കുകളുടെ വിവരങ്ങളും ഇല്ലാതാക്കുമെങ്കിലും കാഴ്ചകളുടെയും കണ്ണിയിലുള്ള ക്ലിക്കുകളുടെ (ഉപഭോക്തൃ വിവരങ്ങളില്ലാതെ) എണ്ണവും നിലനിൽക്കും. അതിനാൽ സ്ഥിതിവിവരക്കണക്കുകളെയും വിശകലനങ്ങളെയും ബാധിക്കില്ല.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "വ്യക്തിഗത വരിക്കാരെ പിൻതുടരുക",
"settings.privacy.individualSubTrackingHelp": "ഉപഭോക്തൃ തലത്തിലുള്ള ക്യാമ്പെയ്ൻ കാഴ്ചകളും കണ്ണിയിലെ ക്ലിക്കുകളും പിൻതുടരുക. അപ്രാപ്‌തമാക്കിയാൽ ക്യാമ്പെയ്ൻ കാഴ്ചകളും കണ്ണികളിന്മേലുള്ള ക്ലിക്കുകളുടെ വിവരങ്ങളും രേഖപ്പെടുത്തുമെങ്കുലും ഉപഭോക്താക്കളുടെ വിവരങ്ങളോട് ചേർക്കില്ല.",
"settings.privacy.listUnsubHeader": "`List-Unsubscribe` തലക്കെട്ട് കൂട്ടിച്ചേർക്കുക",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "വരിക്കാരനെ തടയുന്ന പട്ടികയിൽ ചേർക്കട്ടേ? | {num} വരിക്കാരേ തടയുന്ന പട്ടികയിൽ ചേർക്കട്ടേ?",
"subscribers.confirmDelete": "വരിക്കാരനെ ഇല്ലാതാക്കട്ടെ? | {num} വരിക്കാരേ ഇല്ലാതാക്കട്ടെ?",
"subscribers.confirmExport": "വരിക്കാരനെ എക്സ്പോർട്ട് ചെയ്യട്ടേ? | {num} വരിക്കാരെ എക്സ്പോർട്ട് ചെയ്യട്ടേ?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "ഡാറ്റ ഡൗൺലോഡുചെയ്യുക",
"subscribers.email": "ഇ-മെയിൽ",
"subscribers.emailExists": "ഇ-മെയിൽ നേരത്തേതന്നെ ഉള്ളതാണ്",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Czy zezwolić subskrybentom na eksportowanie danych zebranych o nich?",
"settings.privacy.allowWipe": "Zezwól na czyszczenie danych",
"settings.privacy.allowWipeHelp": "Czy zezwolić subskrybentom na usuwanie ich samych razem z wszystkimi ich danymi? Wyświetlenia i liczba kliknięć zostaną zachowane, ale zostaną z nich usunięte informacje kto wykonał tę akcję.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Śledzenie indywidualnych subskrybentów",
"settings.privacy.individualSubTrackingHelp": "Śledź dane wyświetleń i kliknięć na poziomie pojedynczego subskrybenta. Jeśli wyłączone dane będą nadal zbierane, ale niepowiązane ze subskrybentami.",
"settings.privacy.listUnsubHeader": "Dodawaj nagłówek `List-Unsubscribe`",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Czy zablokować {num} subskrybentów?",
"subscribers.confirmDelete": "Usunąć {num} subskrybentów?",
"subscribers.confirmExport": "Wyeksportować {num} subskrybentów?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Pobierz dane",
"subscribers.email": "Email",
"subscribers.emailExists": "Email już istnieje.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Permitir que os assinantes exportem os dados coletados neles?",
"settings.privacy.allowWipe": "Permitir limpeza",
"settings.privacy.allowWipeHelp": "Permitir que os assinantes se excluam incluindo suas inscrições e todos os outros dados da base de dados. Visualizações da campanha e cliques de links também são removidos enquanto o total de visualizações e cliques permanecem (com nenhum inscrito associado a eles) para que as estatísticas e análises não sejam afetadas.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Rastreamento individual de inscrito",
"settings.privacy.individualSubTrackingHelp": "Rastrear visualizações e cliques de cada inscrito. Quando desativado, o rastreio da visualizações e clique continuar sem estar associado a nenhuma inscrição.",
"settings.privacy.listUnsubHeader": "Incluir cabeçalho `List-Unsubscribe`",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Bloquear {num} inscrito(s)?",
"subscribers.confirmDelete": "Excluir {num} inscrito(s)?",
"subscribers.confirmExport": "Exportar {num} inscrito(s)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Baixar dados",
"subscribers.email": "E-mail",
"subscribers.emailExists": "E-mail já existe.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Permitir aos subscritores exportar os dados coletados neles mesmos?",
"settings.privacy.allowWipe": "Permitir eliminação de dados",
"settings.privacy.allowWipeHelp": "Permitir aos subscritores eliminar todos os seus dados, incluindo as suas subscrições, da base de dados. Visualizações de campanhas e cliques em links também são removidos enquanto visualizações e contagem de clicks permanecem (sem nenhum subscritor associado) para que as estatísticas não sejam afetadas.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Tracking individual de subscritores",
"settings.privacy.individualSubTrackingHelp": "Track visualizações e clicked ao nível do subscritor. Quando desligado, visualizações e track de clicks continuam, mas sem estarem associadas a nenhum subscritor.",
"settings.privacy.listUnsubHeader": "Incluir header `List-Unsubscribe`",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Adicionar {num} subscritor(es) à lista de bloqueio?",
"subscribers.confirmDelete": "Eliminar {num} subscritor(es)?",
"subscribers.confirmExport": "Exportar {num} subscritor(es)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Descarregar dados",
"subscribers.email": "E-mail",
"subscribers.emailExists": "E-mail já existe.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Разрешить подписчикам экспортировать собранные на них данные?",
"settings.privacy.allowWipe": "Разрешить удаление",
"settings.privacy.allowWipeHelp": "Разрешить подписчикам удалять себя (включая их подписки и иные данные) из базы данных. Просмотры кампании и клики по ссылкам также удаляются, в то время как просмотры и счетчики кликов остаются (без привязанного к ним подписчика), так что это не влияет на статистику и аналитику.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Отслеживание каждого подписчика",
"settings.privacy.individualSubTrackingHelp": "Отслеживать просмотры и клики на уровне каждого подписчика. Если отключено, просмотры и клики отслеживаются без привязки к конкретным подписчикам.",
"settings.privacy.listUnsubHeader": "Включать заголовок `List-Unsubscribe`",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Заблокировать {num} подписчика(ов)?",
"subscribers.confirmDelete": "Удалить {num} подписчика(ов)?",
"subscribers.confirmExport": "Экспортировать {num} подписчика(ов)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Загрузить данные",
"subscribers.email": "E-mail",
"subscribers.emailExists": "E-mail существует.",

View file

@ -409,6 +409,8 @@
"settings.privacy.allowExportHelp": "Abonelerin üzerlerinde toplanan verileri dışa aktarmalarına izin verin?",
"settings.privacy.allowWipe": "Silmek için izin ver",
"settings.privacy.allowWipeHelp": "Abonelerin, abonelikleri ve veritabanındaki diğer tüm veriler dahil olmak üzere kendilerini silmesine izin verin. Kampanya görüntülemeleri ve bağlantı tıklamaları da, görünümler ve tıklama sayıları kalır (bunlarla ilişkilendirilmiş abone olmadan), böylece istatistikler ve analizler etkilenmez.",
"settings.privacy.domainBlocklist": "Domain blocklist",
"settings.privacy.domainBlocklistHelp": "E-mail addresses with these domains are disallowed from subscribing. Enter one domain per line, eg: somesite.com",
"settings.privacy.individualSubTracking": "Bireysel üye takibi",
"settings.privacy.individualSubTrackingHelp": "Abone düzeyinde kampanya görüntülemelerini ve tıklamalarını izleyin. Devre dışı bırakıldığında, bireysel abonelere bağlanmadan görüntüleme ve tıklama izleme devam eder.",
"settings.privacy.listUnsubHeader": " `List-Unsubscribe` Başlık bilgisini ekle",
@ -434,6 +436,7 @@
"subscribers.confirmBlocklist": "Erişime engelli {num} üye(leri)?",
"subscribers.confirmDelete": "Sil {num} üye(leri)?",
"subscribers.confirmExport": "Dışa aktar {num} üye(leri)?",
"subscribers.domainBlocklisted": "The e-mail domain is blocklisted.",
"subscribers.downloadData": "Veriyi indir",
"subscribers.email": "E-posta",
"subscribers.emailExists": "E-posta zaten mevcut.",

View file

@ -39,6 +39,7 @@ func V2_0_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
if _, err := db.Exec(`
INSERT INTO settings (key, value) VALUES
('app.send_optin_confirmation', 'true'),
('privacy.domain_blocklist', '[]'),
('bounce.enabled', 'false'),
('bounce.webhooks_enabled', 'false'),
('bounce.count', '2'),

View file

@ -24,6 +24,7 @@ import (
"sync"
"github.com/gofrs/uuid"
"github.com/knadh/listmonk/internal/i18n"
"github.com/knadh/listmonk/models"
"github.com/lib/pq"
)
@ -50,8 +51,9 @@ const (
// Importer represents the bulk CSV subscriber import system.
type Importer struct {
opt Options
db *sql.DB
opt Options
db *sql.DB
i18n *i18n.I18n
stop chan bool
status Status
@ -64,6 +66,9 @@ type Options struct {
BlocklistStmt *sql.Stmt
UpdateListDateStmt *sql.Stmt
NotifCB models.AdminNotifCallback
// Lookup table for blocklisted domains.
DomainBlocklist map[string]bool
}
// Session represents a single import session.
@ -123,12 +128,13 @@ var (
)
// New returns a new instance of Importer.
func New(opt Options, db *sql.DB) *Importer {
func New(opt Options, db *sql.DB, i *i18n.I18n) *Importer {
im := Importer{
opt: opt,
stop: make(chan bool, 1),
db: db,
i18n: i,
status: Status{Status: StatusNone, logBuf: bytes.NewBuffer(nil)},
stop: make(chan bool, 1),
}
return &im
}
@ -518,11 +524,12 @@ func (s *Session) LoadCSV(srcPath string, delim rune) error {
}
sub := SubReq{}
// Lowercase to ensure uniqueness in the DB.
sub.Email = strings.ToLower(strings.TrimSpace(row["email"]))
sub.Email = row["email"]
sub.Name = row["name"]
if err := ValidateFields(sub); err != nil {
s.log.Printf("skipping line %d: %v", i, err)
sub, err = s.im.ValidateFields(sub)
if err != nil {
s.log.Printf("skipping line %d: %s: %v", i, sub.Email, err)
continue
}
@ -564,6 +571,51 @@ func (im *Importer) Stop() {
}
}
// ValidateFields validates incoming subscriber field values and returns sanitized fields.
func (im *Importer) ValidateFields(s SubReq) (SubReq, error) {
if len(s.Email) > 1000 {
return s, errors.New(im.i18n.T("subscribers.invalidEmail"))
}
s.Name = strings.TrimSpace(s.Name)
if len(s.Name) == 0 || len(s.Name) > stdInputMaxLen {
return s, errors.New(im.i18n.T("subscribers.invalidName"))
}
em, err := im.SanitizeEmail(s.Email)
if err != nil {
return s, err
}
s.Email = em
return s, nil
}
// SanitizeEmail validates and sanitizes an e-mail string and returns the lowercased,
// e-mail component of an e-mail string.
func (im *Importer) SanitizeEmail(email string) (string, error) {
email = strings.ToLower(strings.TrimSpace(email))
// Since `mail.ParseAddress` parses an email address which can also contain optional name component
// here we check if incoming email string is same as the parsed email.Address. So this eliminates
// any valid email address with name and also valid address with empty name like `<abc@example.com>`.
em, err := mail.ParseAddress(email)
if err != nil || em.Address != email {
return "", errors.New(im.i18n.T("subscribers.invalidEmail"))
}
// Check if the e-mail's domain is blocklisted.
d := strings.Split(em.Address, "@")
if len(d) == 2 {
_, ok := im.opt.DomainBlocklist[d[1]]
if ok {
return "", errors.New(im.i18n.T("subscribers.domainBlocklisted"))
}
}
return em.Address, nil
}
// mapCSVHeaders takes a list of headers obtained from a CSV file, a map of known headers,
// and returns a new map with each of the headers in the known map mapped by the position (0-n)
// in the given CSV list.
@ -584,20 +636,6 @@ func (s *Session) mapCSVHeaders(csvHdrs []string, knownHdrs map[string]bool) map
return hdrKeys
}
// ValidateFields validates incoming subscriber field values.
func ValidateFields(s SubReq) error {
if len(s.Email) > 1000 {
return errors.New(`e-mail too long`)
}
if !IsEmail(s.Email) {
return errors.New(`invalid e-mail "` + s.Email + `"`)
}
if len(s.Name) == 0 || len(s.Name) > stdInputMaxLen {
return errors.New(`invalid or empty name "` + s.Name + `"`)
}
return nil
}
// countLines counts the number of line breaks in a file. This does not
// distinguish between "blank" and non "blank" lines.
// Credit: https://stackoverflow.com/a/24563853
@ -621,14 +659,3 @@ func countLines(r io.Reader) (int, error) {
}
}
}
// IsEmail checks whether the given string is a valid e-mail address.
func IsEmail(email string) bool {
// Since `mail.ParseAddress` parses an email address which can also contain optional name component
// here we check if incoming email string is same as the parsed email.Address. So this eliminates
// any valid email address with name and also valid address with empty name like `<abc@example.com>`.
if em, err := mail.ParseAddress(email); err != nil || em.Address != email {
return false
}
return true
}

View file

@ -193,6 +193,7 @@ INSERT INTO settings (key, value) VALUES
('privacy.allow_export', 'true'),
('privacy.allow_wipe', 'true'),
('privacy.exportable', '["profile", "subscriptions", "campaign_views", "link_clicks"]'),
('privacy.domain_blocklist', '[]'),
('upload.provider', '"filesystem"'),
('upload.filesystem.upload_path', '"uploads"'),
('upload.filesystem.upload_uri', '"/uploads"'),