wg5 0.0.1-security → 8.462.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wg5 might be problematic. Click here for more details.
- package/index.js +29 -0
- package/package.json +15 -3
- package/src/global/js/helper/assignmentData.js +13 -0
- package/src/global/js/helper/stringToLowerCase.js +11 -0
- package/src/global/js/wg.js +8 -0
- package/src/modules/atoms/abstract-control/AbstractControl.js +206 -0
- package/src/modules/atoms/button/Button.js +30 -0
- package/src/modules/atoms/form/Form.js +100 -0
- package/src/modules/atoms/input/Input.js +39 -0
- package/src/modules/atoms/remove-focus-on-click/removeFocusOnClick.js +22 -0
- package/src/modules/molecules/trial-form/trial-form.js +119 -0
- package/src/modules/organisms/footer/footer.js +53 -0
- package/src/modules/organisms/header/header.js +193 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/MenuItem.js +176 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/Menubar.js +178 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/MenubarItem.js +136 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/PopupMenu.js +230 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/helpers.js +23 -0
- package/src/services/cookie.js +18 -0
- package/src/services/geo.js +17 -0
- package/src/services/phone.js +38 -0
- package/src/services/registration.js +71 -0
- package/src/services/tracking.js +27 -0
- package/src/validators/validate.js +100 -0
- package/README.md +0 -5
@@ -0,0 +1,230 @@
|
|
1
|
+
import { MenuItem } from './MenuItem';
|
2
|
+
|
3
|
+
export class PopupMenu {
|
4
|
+
constructor(domNode, controllerObj) {
|
5
|
+
let elementChildren,
|
6
|
+
msgPrefix = 'PopupMenu constructor argument domNode ';
|
7
|
+
|
8
|
+
// Check whether domNode is a DOM element
|
9
|
+
if (!domNode instanceof Element) {
|
10
|
+
throw new TypeError(msgPrefix + 'is not a DOM Element.');
|
11
|
+
}
|
12
|
+
// Check whether domNode has child elements
|
13
|
+
if (domNode.childElementCount === 0) {
|
14
|
+
throw new Error(msgPrefix + 'has no element children.');
|
15
|
+
}
|
16
|
+
// Check whether domNode descendant elements have A elements
|
17
|
+
let childElement = domNode.firstElementChild;
|
18
|
+
while (childElement) {
|
19
|
+
let menuitem = childElement.firstElementChild;
|
20
|
+
if (menuitem && menuitem === 'A') {
|
21
|
+
throw new Error(msgPrefix + 'has descendant elements that are not A elements.');
|
22
|
+
}
|
23
|
+
childElement = childElement.nextElementSibling;
|
24
|
+
}
|
25
|
+
|
26
|
+
this.isMenubar = false;
|
27
|
+
|
28
|
+
this.domNode = domNode;
|
29
|
+
this.controller = controllerObj;
|
30
|
+
|
31
|
+
this.menuitems = []; // See PopupMenu init method
|
32
|
+
this.firstChars = []; // See PopupMenu init method
|
33
|
+
|
34
|
+
this.firstItem = null; // See PopupMenu init method
|
35
|
+
this.lastItem = null; // See PopupMenu init method
|
36
|
+
|
37
|
+
this.hasFocus = false; // See MenuItem handleFocus, handleBlur
|
38
|
+
this.hasHover = false; // See PopupMenu handleMouseover, handleMouseout
|
39
|
+
}
|
40
|
+
|
41
|
+
init() {
|
42
|
+
let childElement, menuElement, menuItem, textContent, numItems, label;
|
43
|
+
|
44
|
+
// Configure the domNode itself
|
45
|
+
|
46
|
+
this.domNode.parentNode.addEventListener('mouseover', this.handleMouseover.bind(this));
|
47
|
+
this.domNode.parentNode.addEventListener('mouseout', (event) => {
|
48
|
+
this.handleMouseout(event);
|
49
|
+
});
|
50
|
+
|
51
|
+
// Traverse the element children of domNode: configure each with
|
52
|
+
// menuitem role behavior and store reference in menuitems array.
|
53
|
+
childElement = this.domNode.querySelectorAll('.header-nav-sub-menu__item');
|
54
|
+
|
55
|
+
[].forEach.call(childElement, (element) => {
|
56
|
+
menuElement = element.firstElementChild;
|
57
|
+
|
58
|
+
if (menuElement && menuElement.tagName === 'A') {
|
59
|
+
menuItem = new MenuItem(menuElement, this);
|
60
|
+
menuItem.init();
|
61
|
+
this.menuitems.push(menuItem);
|
62
|
+
textContent = menuElement.textContent.trim();
|
63
|
+
this.firstChars.push(textContent.substring(0, 1).toLowerCase());
|
64
|
+
}
|
65
|
+
});
|
66
|
+
|
67
|
+
// Use populated menuitems array to initialize firstItem and lastItem.
|
68
|
+
numItems = this.menuitems.length;
|
69
|
+
if (numItems > 0) {
|
70
|
+
this.firstItem = this.menuitems[0];
|
71
|
+
this.lastItem = this.menuitems[numItems - 1];
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
handleMouseover() {
|
76
|
+
this.hasHover = true;
|
77
|
+
}
|
78
|
+
|
79
|
+
handleMouseout(event) {
|
80
|
+
let currentTarget = event.currentTarget;
|
81
|
+
|
82
|
+
if (!currentTarget.contains(event.relatedTarget)) {
|
83
|
+
this.hasHover = false;
|
84
|
+
this.close(false);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
setFocusToController(command, flag) {
|
89
|
+
if (typeof command !== 'string') {
|
90
|
+
command = '';
|
91
|
+
}
|
92
|
+
|
93
|
+
function setFocusToMenubarItem(controller, close) {
|
94
|
+
while (controller) {
|
95
|
+
if (controller.isMenubarItem) {
|
96
|
+
controller.domNode.focus();
|
97
|
+
return controller;
|
98
|
+
} else {
|
99
|
+
if (close) {
|
100
|
+
controller.menu.close(true);
|
101
|
+
}
|
102
|
+
controller.hasFocus = false;
|
103
|
+
}
|
104
|
+
controller = controller.menu.controller;
|
105
|
+
}
|
106
|
+
return false;
|
107
|
+
}
|
108
|
+
|
109
|
+
if (command === '') {
|
110
|
+
if (this.controller && this.controller.domNode) {
|
111
|
+
this.controller.domNode.focus();
|
112
|
+
}
|
113
|
+
return;
|
114
|
+
}
|
115
|
+
|
116
|
+
if (!this.controller.isMenubarItem) {
|
117
|
+
this.controller.domNode.focus();
|
118
|
+
this.close();
|
119
|
+
|
120
|
+
if (command === 'next') {
|
121
|
+
let menubarItem = setFocusToMenubarItem(this.controller, false);
|
122
|
+
if (menubarItem) {
|
123
|
+
menubarItem.menu.setFocusToNextItem(menubarItem, flag);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
} else {
|
127
|
+
if (command === 'previous') {
|
128
|
+
this.controller.menu.setFocusToPreviousItem(this.controller, flag);
|
129
|
+
} else if (command === 'next') {
|
130
|
+
this.controller.menu.setFocusToNextItem(this.controller, flag);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
setFocusToFirstItem() {
|
136
|
+
this.firstItem.domNode.focus();
|
137
|
+
}
|
138
|
+
|
139
|
+
setFocusToLastItem() {
|
140
|
+
this.lastItem.domNode.focus();
|
141
|
+
}
|
142
|
+
|
143
|
+
setFocusToPreviousItem(currentItem) {
|
144
|
+
let index;
|
145
|
+
|
146
|
+
if (currentItem === this.firstItem) {
|
147
|
+
this.lastItem.domNode.focus();
|
148
|
+
} else {
|
149
|
+
index = this.menuitems.indexOf(currentItem);
|
150
|
+
this.menuitems[index - 1].domNode.focus();
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
setFocusToNextItem(currentItem) {
|
155
|
+
let index;
|
156
|
+
|
157
|
+
if (currentItem === this.lastItem) {
|
158
|
+
this.firstItem.domNode.focus();
|
159
|
+
} else {
|
160
|
+
index = this.menuitems.indexOf(currentItem);
|
161
|
+
this.menuitems[index + 1].domNode.focus();
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
setFocusByFirstCharacter(currentItem, char) {
|
166
|
+
let start,
|
167
|
+
index,
|
168
|
+
lowerCasedChar = char.toLowerCase();
|
169
|
+
|
170
|
+
// Get start index for search based on position of currentItem
|
171
|
+
start = this.menuitems.indexOf(currentItem) + 1;
|
172
|
+
if (start === this.menuitems.length) {
|
173
|
+
start = 0;
|
174
|
+
}
|
175
|
+
|
176
|
+
// Check remaining slots in the menu
|
177
|
+
index = this.getIndexFirstChars(start, lowerCasedChar);
|
178
|
+
|
179
|
+
// If not found in remaining slots, check from beginning
|
180
|
+
if (index === -1) {
|
181
|
+
index = this.getIndexFirstChars(0, lowerCasedChar);
|
182
|
+
}
|
183
|
+
|
184
|
+
// If match was found...
|
185
|
+
if (index > -1) {
|
186
|
+
this.menuitems[index].domNode.focus();
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
getIndexFirstChars(startIndex, char) {
|
191
|
+
for (let i = startIndex; i < this.firstChars.length; i++) {
|
192
|
+
if (char === this.firstChars[i]) {
|
193
|
+
return i;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
return -1;
|
197
|
+
}
|
198
|
+
|
199
|
+
open() {
|
200
|
+
this.domNode.parentNode.classList.add('header-menu__item--opened');
|
201
|
+
this.controller.setExpanded(true);
|
202
|
+
}
|
203
|
+
|
204
|
+
close(force) {
|
205
|
+
let controllerHasHover = this.controller.hasHover;
|
206
|
+
|
207
|
+
let hasFocus = this.hasFocus;
|
208
|
+
|
209
|
+
for (let i = 0; i < this.menuitems.length; i++) {
|
210
|
+
let mi = this.menuitems[i];
|
211
|
+
if (mi.popupMenu) {
|
212
|
+
hasFocus = hasFocus | mi.popupMenu.hasFocus;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
if (!this.controller.isMenubarItem) {
|
217
|
+
controllerHasHover = false;
|
218
|
+
}
|
219
|
+
|
220
|
+
if (force || (!hasFocus && !this.hasHover && !controllerHasHover)) {
|
221
|
+
this.domNode.parentNode.classList.remove('header-menu__item--opened');
|
222
|
+
this.controller.setExpanded(false);
|
223
|
+
}
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
|
229
|
+
// WEBPACK FOOTER //
|
230
|
+
// ./node_modules/wg5/src/modules/organisms/header/scripts/a11y-navigation/PopupMenu.js
|
@@ -0,0 +1,23 @@
|
|
1
|
+
export function isPrintableCharacter(str) {
|
2
|
+
return str.length === 1 && str.match(/\S/);
|
3
|
+
}
|
4
|
+
|
5
|
+
export const keyCode = Object.freeze({
|
6
|
+
TAB: 9,
|
7
|
+
RETURN: 13,
|
8
|
+
ESC: 27,
|
9
|
+
SPACE: 32,
|
10
|
+
PAGEUP: 33,
|
11
|
+
PAGEDOWN: 34,
|
12
|
+
END: 35,
|
13
|
+
HOME: 36,
|
14
|
+
LEFT: 37,
|
15
|
+
UP: 38,
|
16
|
+
RIGHT: 39,
|
17
|
+
DOWN: 40,
|
18
|
+
});
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
// WEBPACK FOOTER //
|
23
|
+
// ./node_modules/wg5/src/modules/organisms/header/scripts/a11y-navigation/helpers.js
|
@@ -0,0 +1,18 @@
|
|
1
|
+
export class CookieService {
|
2
|
+
static getCookie(name) {
|
3
|
+
// TODO: update cookies methods not from WG3
|
4
|
+
// https://www.wrike.com/open.htm?id=522917906
|
5
|
+
return WG.utils.getCookie(name);
|
6
|
+
}
|
7
|
+
static setCookie(name, value, options = {}) {
|
8
|
+
WG.utils.setCookie(name, value, options);
|
9
|
+
}
|
10
|
+
static deleteCookie(name) {
|
11
|
+
return WG.utils.deleteCookie(name);
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
// WEBPACK FOOTER //
|
18
|
+
// ./node_modules/wg5/src/services/cookie.js
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { CookieService } from './cookie';
|
2
|
+
|
3
|
+
export class GeoService {
|
4
|
+
static get country() {
|
5
|
+
return CookieService.getCookie('country');
|
6
|
+
}
|
7
|
+
static get countryCode() {
|
8
|
+
const countryCookie = GeoService.country || 'US:::';
|
9
|
+
|
10
|
+
return countryCookie.split(':')[0];
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
// WEBPACK FOOTER //
|
17
|
+
// ./node_modules/wg5/src/services/geo.js
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import {
|
2
|
+
parsePhoneNumberFromString,
|
3
|
+
parseIncompletePhoneNumber,
|
4
|
+
getExampleNumber,
|
5
|
+
isSupportedCountry,
|
6
|
+
} from 'libphonenumber-js/min';
|
7
|
+
import { GeoService } from './geo';
|
8
|
+
|
9
|
+
import examples from 'libphonenumber-js/examples.mobile.json';
|
10
|
+
|
11
|
+
export class PhoneService {
|
12
|
+
constructor() {
|
13
|
+
this.countryCode = GeoService.countryCode;
|
14
|
+
|
15
|
+
if (isSupportedCountry(this.countryCode) === false) {
|
16
|
+
this.countryCode = 'US';
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
format(text) {
|
21
|
+
let formattedPhone = parsePhoneNumberFromString(text, this.countryCode);
|
22
|
+
|
23
|
+
if (!formattedPhone) {
|
24
|
+
return parseIncompletePhoneNumber(text);
|
25
|
+
}
|
26
|
+
|
27
|
+
return formattedPhone.formatInternational();
|
28
|
+
}
|
29
|
+
|
30
|
+
getExample() {
|
31
|
+
return getExampleNumber(this.countryCode, examples).formatInternational();
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
// WEBPACK FOOTER //
|
38
|
+
// ./node_modules/wg5/src/services/phone.js
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import { stringToLowerCase } from '../global/js/helper/stringToLowerCase';
|
2
|
+
|
3
|
+
export class RegistrationService {
|
4
|
+
static register(email, formName, plan = 'pro', promocode, addons, packages, source, registrationType, template) {
|
5
|
+
return new Promise((resolve, reject) => {
|
6
|
+
const registration = new WG.RebrandingRegistration({
|
7
|
+
fields: {
|
8
|
+
registrationType: true,
|
9
|
+
licenseId: true,
|
10
|
+
},
|
11
|
+
});
|
12
|
+
|
13
|
+
plan = stringToLowerCase(plan);
|
14
|
+
|
15
|
+
registration.setOptions({
|
16
|
+
locale: window.wrike.locale,
|
17
|
+
fields: {
|
18
|
+
registrationType: registrationType,
|
19
|
+
source: source,
|
20
|
+
promoCode: promocode,
|
21
|
+
packages: packages,
|
22
|
+
addons: addons,
|
23
|
+
plan: plan,
|
24
|
+
template: template,
|
25
|
+
},
|
26
|
+
});
|
27
|
+
registration
|
28
|
+
.register(email, formName, plan)
|
29
|
+
.then((response) => {
|
30
|
+
registration.signupSuccessHandler(response);
|
31
|
+
resolve(response);
|
32
|
+
})
|
33
|
+
.fail((response) => {
|
34
|
+
reject(response);
|
35
|
+
});
|
36
|
+
});
|
37
|
+
}
|
38
|
+
|
39
|
+
static registerNewContract(email, formName, plan, addons, packages, source, template, promocode, onboarding) {
|
40
|
+
return new Promise((resolve, reject) => {
|
41
|
+
const registration = new WG.RebrandingRegistrationNewContract();
|
42
|
+
|
43
|
+
registration.setOptions({
|
44
|
+
locale: window.wrike.locale,
|
45
|
+
fields: {
|
46
|
+
plan: plan,
|
47
|
+
addons: addons,
|
48
|
+
packages: packages,
|
49
|
+
template: template,
|
50
|
+
source: source,
|
51
|
+
promoCode: promocode,
|
52
|
+
onboarding: onboarding,
|
53
|
+
},
|
54
|
+
});
|
55
|
+
registration
|
56
|
+
.register(email)
|
57
|
+
.then((response) => {
|
58
|
+
registration.signupSuccessHandler(response);
|
59
|
+
resolve(response);
|
60
|
+
})
|
61
|
+
.fail((response) => {
|
62
|
+
reject(response);
|
63
|
+
});
|
64
|
+
});
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
// WEBPACK FOOTER //
|
71
|
+
// ./node_modules/wg5/src/services/registration.js
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import WG from '../global/js/wg';
|
2
|
+
|
3
|
+
export class TrackingService {
|
4
|
+
/**
|
5
|
+
* trackEventForm
|
6
|
+
* @param {object} options - Options
|
7
|
+
* @param {boolean} enqueue - Enqueue
|
8
|
+
* @return {Promise}
|
9
|
+
* */
|
10
|
+
static trackEventForm(options, enqueue = true) {
|
11
|
+
const tracking = new WG.Tracking(options);
|
12
|
+
return tracking.send(enqueue);
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* trackAirPr
|
17
|
+
* @param {object} options - Options
|
18
|
+
* */
|
19
|
+
static trackAirPr(options) {
|
20
|
+
new WG.AirPrTracking(options);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
// WEBPACK FOOTER //
|
27
|
+
// ./node_modules/wg5/src/services/tracking.js
|
@@ -0,0 +1,100 @@
|
|
1
|
+
function isEmptyInputValue(value) {
|
2
|
+
return value === null || value.length === 0;
|
3
|
+
}
|
4
|
+
const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
5
|
+
|
6
|
+
export class Validators {
|
7
|
+
static min(min) {
|
8
|
+
return (control) => {
|
9
|
+
if (isEmptyInputValue(control) || isEmptyInputValue(min)) {
|
10
|
+
return null;
|
11
|
+
}
|
12
|
+
const value = parseFloat(control);
|
13
|
+
|
14
|
+
return !isNaN(value) && value < min ? { min: { min: min, actual: control } } : null;
|
15
|
+
};
|
16
|
+
}
|
17
|
+
|
18
|
+
static max(max) {
|
19
|
+
return (control) => {
|
20
|
+
if (isEmptyInputValue(control) || isEmptyInputValue(max)) {
|
21
|
+
return null;
|
22
|
+
}
|
23
|
+
const value = parseFloat(control);
|
24
|
+
|
25
|
+
return !isNaN(value) && value > max ? { max: { max: max, actual: control } } : null;
|
26
|
+
};
|
27
|
+
}
|
28
|
+
|
29
|
+
static required(control) {
|
30
|
+
return isEmptyInputValue(control) ? { required: true } : null;
|
31
|
+
}
|
32
|
+
|
33
|
+
static requiredTrue(control) {
|
34
|
+
return control === true ? null : { required: true };
|
35
|
+
}
|
36
|
+
|
37
|
+
static email(control) {
|
38
|
+
if (isEmptyInputValue(control)) {
|
39
|
+
return null;
|
40
|
+
}
|
41
|
+
return EMAIL_REGEXP.test(control) ? null : { email: true };
|
42
|
+
}
|
43
|
+
|
44
|
+
static minLength(minLength) {
|
45
|
+
return (control) => {
|
46
|
+
if (isEmptyInputValue(control)) {
|
47
|
+
return null;
|
48
|
+
}
|
49
|
+
const length = control ? control.length : 0;
|
50
|
+
|
51
|
+
return length < minLength ? { minlength: { requiredLength: minLength, actualLength: length } } : null;
|
52
|
+
};
|
53
|
+
}
|
54
|
+
|
55
|
+
static maxLength(maxLength) {
|
56
|
+
return (control) => {
|
57
|
+
const length = control ? control.length : 0;
|
58
|
+
|
59
|
+
return length > maxLength ? { maxlength: { requiredLength: maxLength, actualLength: length } } : null;
|
60
|
+
};
|
61
|
+
}
|
62
|
+
|
63
|
+
static pattern(pattern) {
|
64
|
+
if (!pattern) return Validators.nullValidator;
|
65
|
+
let regex;
|
66
|
+
let regexStr;
|
67
|
+
|
68
|
+
if (typeof pattern === 'string') {
|
69
|
+
regexStr = '';
|
70
|
+
|
71
|
+
if (pattern.charAt(0) !== '^') regexStr += '^';
|
72
|
+
|
73
|
+
regexStr += pattern;
|
74
|
+
|
75
|
+
if (pattern.charAt(pattern.length - 1) !== '$') regexStr += '$';
|
76
|
+
|
77
|
+
regex = new RegExp(regexStr);
|
78
|
+
} else {
|
79
|
+
regexStr = pattern.toString();
|
80
|
+
regex = pattern;
|
81
|
+
}
|
82
|
+
return (control) => {
|
83
|
+
if (isEmptyInputValue(control)) {
|
84
|
+
return null;
|
85
|
+
}
|
86
|
+
const value = control;
|
87
|
+
|
88
|
+
return regex.test(value) ? null : { pattern: { requiredPattern: regexStr, actualValue: value } };
|
89
|
+
};
|
90
|
+
}
|
91
|
+
|
92
|
+
static nullValidator() {
|
93
|
+
return null;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
// WEBPACK FOOTER //
|
100
|
+
// ./node_modules/wg5/src/validators/validate.js
|
package/README.md
DELETED
@@ -1,5 +0,0 @@
|
|
1
|
-
# Security holding package
|
2
|
-
|
3
|
-
This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
|
4
|
-
|
5
|
-
Please refer to www.npmjs.com/advisories?search=wg5 for more information.
|