├── .gitignore ├── .travis.yml ├── index.js ├── src ├── helpers │ └── trim_ip_address.js ├── netplan │ ├── index.js │ └── config.js └── index.js ├── test ├── helpers │ └── trim_ip_address_spec.js ├── netplan │ ├── index_test.js │ └── config_test.js └── index_test.js ├── package.json ├── LICENSE ├── CHANGELOG.md ├── README.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | *.swp 3 | 4 | tags 5 | tags.* 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var index = require('./src/index.js') 4 | 5 | module.exports = index 6 | -------------------------------------------------------------------------------- /src/helpers/trim_ip_address.js: -------------------------------------------------------------------------------- 1 | var r = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g 2 | 3 | module.exports = ip => { 4 | try { 5 | return ip ? ip.match(r)[0] : '' 6 | } catch (e) { 7 | throw new Error('Invalid IP Address: ' + ip) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/helpers/trim_ip_address_spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var { expect } = require('chai') 4 | var trim_ip_address = require('../../src/helpers/trim_ip_address.js') 5 | 6 | describe('helperes/trim_ip_address.js', () => { 7 | it('should return ip address', () => { 8 | expect(trim_ip_address(' 10.0.0.1')).to.eql('10.0.0.1') 9 | expect(trim_ip_address(' 10.0.0.1x')).to.eql('10.0.0.1') 10 | expect(trim_ip_address('x10.0.0.1x')).to.eql('10.0.0.1') 11 | }) 12 | it ('should return blank if ip is undefined', () => { 13 | expect(trim_ip_address(undefined)).to.eql('') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@adopisoft/netplan", 3 | "version": "2.0.3", 4 | "description": "Node module for setting up network interface(s) ip address.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --recursive" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/adonespitogo/node-set-ip-address.git" 12 | }, 13 | "keywords": [ 14 | "ip", 15 | "address", 16 | "network", 17 | "dhcp", 18 | "static" 19 | ], 20 | "author": "Adones Pitogo", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/adonespitogo/node-set-ip-address/issues" 24 | }, 25 | "homepage": "https://github.com/adonespitogo/node-set-ip-address#readme", 26 | "devDependencies": { 27 | "assign-deep": "^1.0.1", 28 | "chai": "^4.2.0", 29 | "mocha": "^10.1.0", 30 | "proxyquire": "^2.1.3", 31 | "sinon": "^7.5.0" 32 | }, 33 | "dependencies": { 34 | "js-yaml": "^3.13.1", 35 | "make-dir": "^3.0.0", 36 | "netmask": "^2.0.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Adones B. Pitogo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/netplan/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var fs = require('fs') 4 | var yaml = require('js-yaml') 5 | var config = require('./config.js') 6 | var util = require('util') 7 | var readdir = util.promisify(fs.readdir) 8 | var writeFile = util.promisify(fs.writeFile) 9 | var ensureDir = require('make-dir') 10 | var { exec } = require('child_process') 11 | var { promisify } = require("util"); 12 | var execPromise = promisify(exec) 13 | 14 | exports.cfg_stack = { 15 | network: { 16 | version: 2, 17 | renderer: 'networkd' 18 | } 19 | } 20 | 21 | exports.getYamlFileName = async () => { 22 | await ensureDir('/etc/netplan') 23 | var files = await readdir('/etc/netplan') 24 | return '/etc/netplan/' + (files[0] || '01-networkcfg.yaml') 25 | } 26 | 27 | exports.setInterface = (cfg) => { 28 | exports.cfg_stack = config.generate(exports.cfg_stack, cfg) 29 | } 30 | 31 | exports.writeConfig = async () => { 32 | var cfg_yaml = yaml.safeDump(exports.cfg_stack, { noCompatMode: true }) 33 | var filename = await exports.getYamlFileName() 34 | await ensureDir('/etc/netplan') 35 | await writeFile(filename, cfg_yaml) 36 | await execPromise(`chmod 600 ${filename}`) 37 | } 38 | 39 | exports.configure = (configs) => { 40 | exports.cfg_stack.network.ethernets = {} 41 | exports.cfg_stack.network.vlans = {} 42 | exports.cfg_stack.network.bridges = {} 43 | configs.forEach(c => { 44 | var cfg = Object.assign({}, c) 45 | return exports.setInterface(cfg) 46 | }) 47 | return exports.writeConfig() 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var child_process = require("child_process"); 3 | var netplan = require("./netplan/index.js"); 4 | var { promisify } = require("util"); 5 | 6 | exports.configure = async (configs) => { 7 | if (typeof configs == "object" && !Array.isArray(configs)) 8 | configs = [configs]; 9 | 10 | var vlans_table = {}; 11 | 12 | configs.forEach((c) => { 13 | if (typeof c.vlanid == "number") { 14 | var k = `${c.interface}.${c.vlanid}`; 15 | if (vlans_table[k]) 16 | throw new Error("Can't have same VLAN ID on interface " + c.interface); 17 | vlans_table[k] = true; 18 | if (Array.isArray(c.bridge_ports)) 19 | throw new Error( 20 | `VLAN ${c.vlanid} in "${c.interface}" cannot have bridged interfaces`, 21 | ); 22 | 23 | var ifname = `${c.interface}.${c.vlanid}`; 24 | if (ifname.length > 15) { 25 | var i = ifname.length - 15; 26 | ifname = ifname.substring(i); 27 | } 28 | c.ifname = ifname; 29 | } 30 | if (Array.isArray(c.bridge_ports)) { 31 | c.bridge_ports.forEach((p) => { 32 | configs.forEach((_c) => { 33 | if ( 34 | _c.interface != c.interface && 35 | Array.isArray(_c.bridge_ports) && 36 | _c.bridge_ports.includes(p) 37 | ) 38 | throw new Error( 39 | `Interface "${p}" is bridged in "${c.interface}" and "${_c.interface}"`, 40 | ); 41 | }); 42 | }); 43 | } 44 | }); 45 | 46 | var sorted = configs 47 | .sort((a, b) => { 48 | return typeof a.vlanid == "number" && typeof b.vlanid != "number" 49 | ? 1 50 | : typeof a.vlanid != "number" && typeof b.vlanid == "number" 51 | ? -1 52 | : 0; 53 | }) 54 | .sort((a, b) => { 55 | var ret = 56 | !Array.isArray(a.bridge_ports) && Array.isArray(b.bridge_ports) 57 | ? -1 58 | : Array.isArray(a.bridge_ports) && !Array.isArray(b.bridge_ports) 59 | ? 1 60 | : 0; 61 | return ret; 62 | }); 63 | 64 | await netplan.configure(sorted); 65 | }; 66 | 67 | exports.restartService = async () => { 68 | var { exec } = child_process; 69 | var execPromise = promisify(exec); 70 | 71 | var error = null; 72 | var network_service_restarted = false; 73 | 74 | await execPromise("netplan apply") 75 | .then(() => (network_service_restarted = true)) 76 | .catch((e) => { 77 | console.log(e); 78 | error = e; 79 | }); 80 | 81 | if (!network_service_restarted) return Promise.reject(error); 82 | }; 83 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | v2.0.3 2 | --- 3 | - Change netplan config file permission to 600 4 | 5 | v2.0.2 6 | --- 7 | - Fix match option of interface not retained if it is a member of a bridge. 8 | 9 | v2.0.1 10 | --- 11 | - Support `match` option for netplan 12 | 13 | v2.0.0 14 | --- 15 | - Removed support for `ifdownup` and `dhcpcd`. Only `netplan` is supported now. 16 | - Removed stp option for bridged interfaces, use `parameters` instead. 17 | 18 | v1.2.3 19 | --- 20 | - Added `optional: true` to physical network interfaces included in bridge ports 21 | 22 | v1.2.2 23 | --- 24 | - (Fix up) `helpers/trim_ip_address.js` return empty string if undefined IP 25 | 26 | v1.2.1 27 | --- 28 | - Don't run restart networking service commands in parallel 29 | 30 | v1.2.0 31 | --- 32 | - Now uses `routes` instead of the deprecated `gateway4` for the default gateway. Fixes #33 33 | 34 | v1.1.1 35 | --- 36 | - Accept `nameservers` option format in string separated by comma or space 37 | 38 | v1.1.0 39 | --- 40 | - Add support for PPPoE interface 41 | 42 | v1.0.10 43 | --- 44 | - Added `dns-nameservers` option to interfaces.d 45 | 46 | v1.0.9 47 | --- 48 | - Added configuration for the loopback interface in `/etc/network/interfaces` 49 | - Support for netplan `optional` param 50 | 51 | v1.0.4-8 52 | --- 53 | - Support for bridge `stp` option 54 | - Use netmask instead of network prefix in /etc/network/interfaces 55 | 56 | v1.0.3 57 | --- 58 | - Update package dependencies and fix vulnerabilities with `npm audit fix` 59 | 60 | v1.0.2 61 | --- 62 | - Allow interface hotplug 63 | 64 | v1.0.1 65 | --- 66 | - Fix error when vlan ifname > 15 characters 67 | 68 | v1.0.0 69 | --- 70 | - Support for bridge interfaces 71 | - Improve interoperability with [Windows Server](https://netplan.io/examples#integration-with-a-windows-dhcp-server) 72 | - Breaking changes: 73 | * Interface configs are no longer saved in individual files under `/etc/network/interfaces.d/`. Instead, they are now written in `/etc/network/interfaces` file. 74 | 75 | v0.2.0 76 | --- 77 | - added ./index.js as main file 78 | 79 | v0.1.7 80 | --- 81 | - vlan validation - must not have the same vlan id on same interface 82 | - accept object param for single interface configuration 83 | 84 | v0.1.6 85 | --- 86 | - fix extra ip address added to interface when it has vlan 87 | 88 | v0.1.5 89 | --- 90 | - optional network restart 91 | 92 | v0.1.4 93 | --- 94 | - accept zero vlan tag/id 95 | 96 | v0.1.3 97 | --- 98 | - fix netplan vlan config not removed 99 | 100 | v0.1.2 101 | --- 102 | - fix netplan vlan feature 103 | 104 | v0.1.1 105 | --- 106 | - restart networking service after config change 107 | 108 | v0.1.0 109 | --- 110 | - support for vlan interface 111 | 112 | Previous Releases 113 | --- 114 | - support for `dhcpcd` (/etc/dhcpcd.conf) 115 | - support for `ifdownup` (/etc/network/interfaces.d/*) 116 | - support for `netplan` (/etc/netplan/*) 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # set-ip-address 2 | Node module for setting up network interface(s) ip address, dns, and default routes of physical network interfaces, VLAN, and bridged interfaces. 3 | 4 | [![Build Status](https://travis-ci.com/adonespitogo/node-set-ip-address.svg?branch=master)](https://travis-ci.com/adonespitogo/node-set-ip-address) 5 | 6 | Supported linux network configurations: 7 | - netplan (/etc/netplan/) 8 | 9 | Install 10 | --- 11 | 12 | ``` 13 | yarn add set-ip-address 14 | ``` 15 | 16 | OR 17 | 18 | 19 | ``` 20 | npm i --save set-ip-address 21 | ``` 22 | 23 | Basic Usage 24 | --- 25 | 26 | ```js 27 | var set_ip_address = require('set-ip-address') 28 | ``` 29 | 30 | ```js 31 | var eth0 = { 32 | interface: 'eth0', 33 | ip_address: '10.0.0.1', 34 | prefix: 20, 35 | gateway: '10.0.0.1', 36 | nameservers: ['8.8.8.8'], // nameservers can also be a string separated by space/comma, ex: `"1.1.1.1, 8.8.8.8 8.8.4.4"` 37 | optional: true, // dont wait for interfaces to avoid boot delay 38 | match: { // netplan match option 39 | name: 'eth0' 40 | } 41 | } 42 | 43 | var eth1 { 44 | interface: 'eth1', 45 | dhcp: true 46 | } 47 | 48 | set_ip_address.configure([eth0, eth1]).then(() => console.log('done writing config files') 49 | 50 | ``` 51 | 52 | Create and Configure VLAN Interface 53 | --- 54 | 55 | You can create vlan interfaces by passing `vlanid` option. Make sure to load `8021q` module to the kernel: 56 | 57 | ``` 58 | sudo modprobe 8021q 59 | ``` 60 | 61 | ```js 62 | var eth0 = { 63 | interface: 'eth0', 64 | ip_address: '10.0.0.1', 65 | prefix: 20, 66 | gateway: '10.0.0.1', 67 | nameservers: ['8.8.8.8'] 68 | } 69 | 70 | var vlan1 { 71 | interface: 'eth0', 72 | vlanid: 10, 73 | ip_address: '20.0.0.1', 74 | prefix: 20, 75 | gateway: '20.0.0.1', 76 | nameservers: ['8.8.8.8'] 77 | } 78 | 79 | set_ip_address.configure([eth0, vlan1]).then(() => console.log('done writing config files') 80 | 81 | ``` 82 | 83 | Create and Configure Bridged Interfaces 84 | --- 85 | 86 | ```js 87 | var eth0 = { 88 | interface: 'eth0', 89 | manual: true 90 | } 91 | 92 | var vlan1 { 93 | interface: 'eth0', 94 | vlanid: 10, 95 | manual: true 96 | } 97 | 98 | var br0 = { 99 | interface: 'br0', 100 | ip_address: '10.0.0.1', 101 | prefix: 20, 102 | gateway: '10.0.0.1', 103 | nameservers: ['8.8.8.8'], 104 | bridge_ports: ['eth0', 'eth0.10'], 105 | bridge_opts: { 106 | parameters: { 107 | stp: true 108 | } 109 | } 110 | } 111 | 112 | set_ip_address 113 | .configure([eth0, vlan1, br0]) 114 | .then(() => console.log('done writing config files') 115 | ``` 116 | 117 | Create and Configure PPPoE Interface 118 | --- 119 | 120 | ```js 121 | var ppp = { 122 | provider: 'dsl-provider', 123 | physical_interface: 'eth0' 124 | } 125 | 126 | set_ip_address 127 | .configure([ppp]) 128 | .then(() => console.log('done writing config files') 129 | 130 | ``` 131 | 132 | Restart Networking Service 133 | --- 134 | 135 | ```js 136 | set_ip_address.restartService() 137 | .then(() => console.log('network service restarted')) 138 | ``` 139 | 140 | LICENSE 141 | --- 142 | 143 | [MIT](LICENSE) 144 | 145 | -------------------------------------------------------------------------------- /src/netplan/config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var trim_ip_address = require("../helpers/trim_ip_address.js"); 4 | 5 | exports.generate = (currentConfig, interfaceConfig) => { 6 | var iface = interfaceConfig.interface; 7 | var is_vlan = typeof interfaceConfig.vlanid === "number"; 8 | var cfg = { 9 | network: { 10 | version: 2, 11 | renderer: "networkd", 12 | ethernets: currentConfig.network.ethernets || {}, 13 | vlans: currentConfig.network.vlans || {}, 14 | bridges: currentConfig.network.bridges || {}, 15 | }, 16 | }; 17 | var config = {}; 18 | 19 | if (interfaceConfig.optional) config.optional = true; 20 | 21 | if (interfaceConfig.match) config.match = interfaceConfig.match; 22 | 23 | if (interfaceConfig.ip_address && !interfaceConfig.dhcp) { 24 | config.dhcp4 = false; 25 | config.dhcp6 = false; 26 | config.addresses = [ 27 | trim_ip_address(interfaceConfig.ip_address) + 28 | "/" + 29 | interfaceConfig.prefix, 30 | ]; 31 | 32 | // dns nameservers 33 | if (interfaceConfig.nameservers) { 34 | var ns = interfaceConfig.nameservers; 35 | var r = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g; 36 | if (typeof ns === "string") { 37 | var addresses = ns.match(r); 38 | config.nameservers = { addresses }; 39 | } else if (Array.isArray(interfaceConfig.nameservers)) { 40 | var addresses = interfaceConfig.nameservers.reduce((result, item) => { 41 | var addrs = item.match(r); 42 | return result.concat(addrs); 43 | }, []); 44 | config.nameservers = { addresses }; 45 | } else { 46 | } 47 | } 48 | // end dns config 49 | if (interfaceConfig.gateway) 50 | config.routes = [{ to: "default", via: interfaceConfig.gateway }]; 51 | } else { 52 | config.dhcp4 = !!interfaceConfig.dhcp; 53 | config.dhcp6 = config.dhcp4; 54 | if (interfaceConfig.dhcp) { 55 | config["dhcp-identifier"] = "mac"; 56 | } 57 | } 58 | 59 | if (!is_vlan) { 60 | if (Array.isArray(interfaceConfig.bridge_ports)) { 61 | interfaceConfig.bridge_opts = interfaceConfig.bridge_opts || {}; 62 | interfaceConfig.bridge_ports.forEach((p) => { 63 | if (cfg.network.vlans[p]) { 64 | var vlan = cfg.network.vlans[p]; 65 | cfg.network.vlans[p] = { 66 | id: vlan.id, 67 | link: vlan.link, 68 | dhcp4: false, 69 | dhcp6: false, 70 | optional: true, 71 | }; 72 | } else { 73 | var prev = cfg.network.ethernets[p]; 74 | var prevcfg = {}; 75 | 76 | if (prev) { 77 | var to_retain = ["match"]; 78 | for (var k of to_retain) { 79 | if (prev[k]) { 80 | prevcfg[k] = prev[k]; 81 | } 82 | } 83 | } 84 | cfg.network.ethernets[p] = Object.assign(prevcfg, { 85 | dhcp4: false, 86 | dhcp6: false, 87 | optional: true, 88 | }); 89 | } 90 | }); 91 | var { parameters } = interfaceConfig.bridge_opts; 92 | config.interfaces = interfaceConfig.bridge_ports; 93 | config.parameters = parameters || {}; 94 | cfg.network.bridges[iface] = config; 95 | } else if (!interfaceConfig.ppp) cfg.network.ethernets[iface] = config; 96 | } else { 97 | config.id = interfaceConfig.vlanid; 98 | config.link = iface; 99 | if (interfaceConfig.optional) { 100 | config.optional = true; 101 | } 102 | 103 | cfg.network.vlans[interfaceConfig.ifname] = config; 104 | } 105 | 106 | return cfg; 107 | }; 108 | -------------------------------------------------------------------------------- /test/netplan/index_test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var sinon = require('sinon') 4 | var proxyquire = require('proxyquire') 5 | var { expect } = require('chai') 6 | 7 | describe('netplan', () => { 8 | 9 | var netplan, 10 | cfg_yaml, 11 | config, 12 | yaml, 13 | dump_ouput, 14 | fs, 15 | filename, 16 | child_process, 17 | ensureDir 18 | 19 | beforeEach(() => { 20 | ensureDir = sinon.fake.resolves() 21 | cfg_yaml = 'some config' 22 | dump_ouput = 'some yaml output' 23 | yaml = { safeDump: sinon.fake(() => dump_ouput) } 24 | config = { generate: sinon.fake.returns(cfg_yaml) } 25 | filename = 'some file' 26 | fs = { 27 | readdir: sinon.fake((dir, cb) => cb(null, [filename])), 28 | writeFile: sinon.fake((file, data, cb) => { 29 | cb() 30 | }) 31 | } 32 | child_process = { 33 | exec: sinon.fake((cmd, opts, cb) => { 34 | if (typeof opts == 'function') { 35 | cb = opts 36 | } 37 | cb() 38 | }) 39 | } 40 | 41 | netplan = proxyquire('../../src/netplan/index.js', { 42 | fs, 43 | child_process, 44 | 'make-dir': ensureDir, 45 | 'js-yaml': yaml, 46 | './config': config 47 | }) 48 | }) 49 | 50 | describe('getYamlFileName()', () => { 51 | it('should return the yaml file name', async () => { 52 | var f = await netplan.getYamlFileName() 53 | sinon.assert.calledWithExactly(ensureDir, '/etc/netplan') 54 | expect(fs.readdir.lastCall.args[0]).to.equal('/etc/netplan') 55 | expect(f).to.equal('/etc/netplan/' + filename) 56 | }) 57 | it('should return random file name', async () => { 58 | fs = { 59 | readdir: sinon.fake((dir, cb) => cb(null, [])) 60 | } 61 | netplan = proxyquire('../../src/netplan/index.js', { 62 | fs, 63 | 'make-dir': ensureDir, 64 | }) 65 | var f = await netplan.getYamlFileName() 66 | sinon.assert.calledWithExactly(ensureDir, '/etc/netplan') 67 | expect(fs.readdir.lastCall.args[0]).to.equal('/etc/netplan') 68 | expect(f).to.equal('/etc/netplan/01-networkcfg.yaml') 69 | }) 70 | }) 71 | 72 | describe('setInterface()', () => { 73 | 74 | it('should stack config for single interface', () => { 75 | var eth0 = { 76 | interface: 'eth0' 77 | } 78 | var arg1 = netplan.cfg_stack 79 | netplan.setInterface(eth0) 80 | sinon.assert.calledWithExactly(config.generate, arg1, eth0) 81 | expect(netplan.cfg_stack).to.eql(cfg_yaml) 82 | }) 83 | 84 | }) 85 | 86 | describe('writeConfig()', () => { 87 | 88 | beforeEach(() => { 89 | sinon.stub(netplan, 'getYamlFileName').returns('/etc/netplan/' + filename) 90 | }) 91 | 92 | afterEach(() => { 93 | netplan.getYamlFileName.restore() 94 | }) 95 | 96 | it('should write config to /etc/netplan', async () => { 97 | await netplan.writeConfig() 98 | sinon.assert.calledWithExactly(ensureDir, '/etc/netplan') 99 | sinon.assert.calledWithExactly(yaml.safeDump, netplan.cfg_stack, { noCompatMode: true }) 100 | expect(fs.writeFile.lastCall.args[0]).to.equal('/etc/netplan/' + filename) 101 | expect(fs.writeFile.lastCall.args[1]).to.equal(dump_ouput) 102 | expect(fs.writeFile.lastCall.args[1]).to.equal(dump_ouput) 103 | expect(child_process.exec.lastCall.args[0]).to.equal(`chmod 600 /etc/netplan/${filename}`) 104 | }) 105 | 106 | }) 107 | 108 | describe('configure()', () => { 109 | var set_interface_stub, write_stub 110 | beforeEach(() => { 111 | set_interface_stub = sinon.stub(netplan, 'setInterface').returns() 112 | write_stub = sinon.stub(netplan, 'writeConfig').resolves() 113 | }) 114 | afterEach(() => { 115 | set_interface_stub.restore() 116 | write_stub.restore() 117 | }) 118 | it('should accept array arg', async () => { 119 | netplan.cfg_stack = { 120 | network: { 121 | ethernets: ['some config'], 122 | vlans: ['some vlans'] 123 | } 124 | } 125 | var eth0 = { interface: 'eth0', ip_address: '10.0.0.1' } 126 | var eth1 = { interface: 'eth1', ip_address: '10.0.0.1' } 127 | var configs = [eth0, eth1] 128 | await netplan.configure(configs) 129 | expect(set_interface_stub.firstCall.args).to.eql([eth0]) 130 | expect(set_interface_stub.lastCall.args).to.eql([eth1]) 131 | expect(netplan.cfg_stack.network.ethernets).to.eql({}) 132 | expect(netplan.cfg_stack.network.vlans).to.eql({}) 133 | sinon.assert.calledOnce(write_stub) 134 | sinon.assert.callOrder(set_interface_stub, write_stub) 135 | }) 136 | 137 | }) 138 | 139 | }) 140 | -------------------------------------------------------------------------------- /test/index_test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var sinon = require("sinon"); 4 | var proxyquire = require("proxyquire"); 5 | var { expect } = require("chai"); 6 | 7 | describe("index.js", () => { 8 | var set_ip_address, netplan, child_process; 9 | 10 | beforeEach(() => { 11 | netplan = { configure: sinon.fake.resolves() }; 12 | child_process = {}; 13 | 14 | set_ip_address = proxyquire("../src/index.js", { 15 | "./netplan/index.js": netplan, 16 | child_process: child_process, 17 | }); 18 | }); 19 | 20 | describe("index.js", () => { 21 | it("should equal src/index.js", () => { 22 | var index_src = require("../src/index.js"); 23 | var index = require("../index.js"); 24 | expect(index_src).to.eql(index); 25 | }); 26 | }); 27 | 28 | describe("configure()", () => { 29 | var restart_stub; 30 | var restart_result; 31 | beforeEach(() => { 32 | restart_result = "ok"; 33 | restart_stub = sinon 34 | .stub(set_ip_address, "restartService") 35 | .resolves(restart_result); 36 | }); 37 | 38 | afterEach(() => { 39 | restart_stub.restore(); 40 | }); 41 | 42 | it("should order configs, physical interface first then vlans, then bridge interfaces", async () => { 43 | var configs = [ 44 | { interface: "eth0" }, 45 | { interface: "eth0", vlanid: 10 }, 46 | { interface: "br0", bridge_ports: ["eth0"] }, 47 | { interface: "eth1" }, 48 | { interface: "eth1", vlanid: 10 }, 49 | ]; 50 | var expected_configs = [ 51 | { interface: "eth0" }, 52 | { interface: "eth1" }, 53 | { interface: "eth0", vlanid: 10, ifname: "eth0.10" }, 54 | { interface: "eth1", vlanid: 10, ifname: "eth1.10" }, 55 | { interface: "br0", bridge_ports: ["eth0"] }, 56 | ]; 57 | await set_ip_address.configure(configs); 58 | sinon.assert.calledWithExactly(netplan.configure, expected_configs); 59 | expected_configs.forEach((c, i) => { 60 | expect(netplan.configure.firstCall.args[0][i]).to.eql( 61 | expected_configs[i], 62 | ); 63 | }); 64 | sinon.assert.notCalled(restart_stub); 65 | }); 66 | 67 | it("should reject if one interface contains same vlan id", async () => { 68 | var configs = [ 69 | { interface: "eth0" }, 70 | { interface: "eth0", vlanid: 10, ifname: "eth0.1" }, 71 | { interface: "eth1" }, 72 | { interface: "eth0", vlanid: 10, ifname: "eth0.1" }, 73 | ]; 74 | try { 75 | await set_ip_address.configure(configs); 76 | expect.fail(); 77 | } catch (e) { 78 | expect(e.message).to.equal("Can't have same VLAN ID on interface eth0"); 79 | sinon.assert.notCalled(netplan.configure); 80 | sinon.assert.notCalled(restart_stub); 81 | } 82 | }); 83 | 84 | it("should reject if bridge_ports is overlapping", async () => { 85 | try { 86 | var configs = [ 87 | { interface: "br0", dhcp: true, bridge_ports: ["eth0"] }, 88 | { interface: "br1", dhcp: true, bridge_ports: ["eth0"] }, 89 | { interface: "eth1", dhcp: true }, 90 | ]; 91 | await set_ip_address.configure(configs); 92 | expect.fail(); 93 | } catch (e) { 94 | expect(e).to.be.an("error"); 95 | expect(e.message).to.equal( 96 | `Interface "eth0" is bridged in "br0" and "br1"`, 97 | ); 98 | } 99 | }); 100 | 101 | it("should reject if vlan has bridge_ports", async () => { 102 | try { 103 | var configs = [ 104 | { interface: "eth0", dhcp: true }, 105 | { interface: "eth1", dhcp: true, vlanid: 10, bridge_ports: ["eth0"] }, 106 | ]; 107 | await set_ip_address.configure(configs); 108 | expect.fail(); 109 | } catch (e) { 110 | expect(e).to.be.an("error"); 111 | expect(e.message).to.equal( 112 | `VLAN 10 in "eth1" cannot have bridged interfaces`, 113 | ); 114 | } 115 | }); 116 | 117 | it("should set vlan ifname", async () => { 118 | var eth0_vlan = { 119 | interface: "eth0", 120 | vlanid: 10, 121 | dhcp: true, 122 | }; 123 | var eth1_vlan = { 124 | interface: "enx00e04c534458", 125 | vlanid: 10, 126 | dhcp: true, 127 | }; 128 | var configs = [eth0_vlan, eth1_vlan]; 129 | var expected_configs = [ 130 | { interface: "eth0", ifname: "eth0.10", vlanid: 10, dhcp: true }, 131 | { 132 | interface: "enx00e04c534458", 133 | ifname: "00e04c534458.10", 134 | vlanid: 10, 135 | dhcp: true, 136 | }, 137 | ]; 138 | await set_ip_address.configure(configs); 139 | sinon.assert.calledWithExactly(netplan.configure, expected_configs); 140 | }); 141 | 142 | it("should call .configure method of netplan module", async () => { 143 | var eth0 = { interface: "eth0", ip_address: "10.0.0.1" }; 144 | var eth1 = { interface: "eth1", ip_address: "10.0.0.1" }; 145 | var configs = [eth0, eth1]; 146 | await set_ip_address.configure(configs); 147 | sinon.assert.calledWithExactly(netplan.configure, configs); 148 | sinon.assert.notCalled(restart_stub); 149 | }); 150 | 151 | it("should accept single config object", async () => { 152 | var eth0 = { interface: "eth0", ip_address: "10.0.0.1" }; 153 | var configs = [eth0]; 154 | await set_ip_address.configure(eth0); 155 | sinon.assert.calledWithExactly(netplan.configure, configs); 156 | sinon.assert.notCalled(restart_stub); 157 | }); 158 | }); 159 | 160 | }); 161 | -------------------------------------------------------------------------------- /test/netplan/config_test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var { expect } = require("chai"); 4 | 5 | describe("netplan", () => { 6 | var templates, defaults; 7 | 8 | beforeEach(() => { 9 | templates = require("../../src/netplan/config.js"); 10 | 11 | defaults = { 12 | network: { 13 | version: 2, 14 | renderer: "networkd", 15 | ethernets: {}, 16 | }, 17 | }; 18 | }); 19 | 20 | it("it should generate config for single interface with no gateway", () => { 21 | var config = { 22 | interface: "eth0", 23 | ip_address: "10.0.0.1 ", 24 | prefix: 20, 25 | nameservers: ["10.0.0.1"], 26 | optional: true, 27 | match: { 28 | macaddress: "xxx", 29 | }, 30 | }; 31 | var expected_ethernets = { 32 | eth0: { 33 | dhcp4: false, 34 | dhcp6: false, 35 | addresses: ["10.0.0.1/20"], 36 | nameservers: { 37 | addresses: config.nameservers, 38 | }, 39 | optional: true, 40 | match: { 41 | macaddress: "xxx", 42 | }, 43 | }, 44 | }; 45 | expect(templates.generate(defaults, config).network.ethernets).to.eql( 46 | expected_ethernets, 47 | ); 48 | }); 49 | 50 | it("it should generate config for single interface with multiple dns separated by space", () => { 51 | var config = { 52 | interface: "eth0", 53 | ip_address: " 10.0.0.1", 54 | prefix: 20, 55 | nameservers: " 10.0.0.1 8.8.8.8 ", 56 | optional: true, 57 | }; 58 | var expected_ethernets = { 59 | eth0: { 60 | dhcp4: false, 61 | dhcp6: false, 62 | addresses: ["10.0.0.1/20"], 63 | nameservers: { 64 | addresses: ["10.0.0.1", "8.8.8.8"], 65 | }, 66 | optional: true, 67 | }, 68 | }; 69 | expect(templates.generate(defaults, config).network.ethernets).to.eql( 70 | expected_ethernets, 71 | ); 72 | }); 73 | 74 | it("it should generate config for single interface with multiple dns in array", () => { 75 | var config = { 76 | interface: "eth0", 77 | ip_address: "10.0.0.1", 78 | prefix: 20, 79 | nameservers: [" 10.0.0.1 8.8.8.8 "], 80 | optional: true, 81 | }; 82 | var expected_ethernets = { 83 | eth0: { 84 | dhcp4: false, 85 | dhcp6: false, 86 | addresses: ["10.0.0.1/20"], 87 | nameservers: { 88 | addresses: ["10.0.0.1", "8.8.8.8"], 89 | }, 90 | optional: true, 91 | }, 92 | }; 93 | expect(templates.generate(defaults, config).network.ethernets).to.eql( 94 | expected_ethernets, 95 | ); 96 | }); 97 | 98 | it("it should generate config for 2nd interface with gateway", () => { 99 | var eth0 = { 100 | dhcp4: false, 101 | dhcp6: false, 102 | addresses: ["10.0.0.1/20"], 103 | nameservers: { 104 | addresses: ["10.0.0.1"], 105 | }, 106 | }; 107 | 108 | defaults.network.ethernets.eth0 = eth0; 109 | 110 | var eth1_config = { 111 | interface: "eth1", 112 | ip_address: "10.0.0.1", 113 | prefix: 20, 114 | nameservers: ["10.0.0.1"], 115 | gateway: "10.0.0.1 ", 116 | optional: true, 117 | }; 118 | 119 | var expected_ethernets = { 120 | eth0: { ...eth0 }, 121 | eth1: { 122 | dhcp4: false, 123 | dhcp6: false, 124 | addresses: ["10.0.0.1/20"], 125 | routes: [{ to: "default", via: eth1_config.gateway }], 126 | nameservers: { 127 | addresses: eth1_config.nameservers, 128 | }, 129 | optional: true, 130 | }, 131 | }; 132 | expect(templates.generate(defaults, eth1_config).network.ethernets).to.eql( 133 | expected_ethernets, 134 | ); 135 | }); 136 | 137 | it("it should generate config for 2nd interface with multiple dns separated by comma", () => { 138 | var eth0 = { 139 | dhcp4: false, 140 | dhcp6: false, 141 | addresses: ["10.0.0.1/20"], 142 | nameservers: { 143 | addresses: ["10.0.0.1"], 144 | }, 145 | }; 146 | var eth1_config = { 147 | interface: "eth1", 148 | ip_address: "10.0.0.1", 149 | prefix: 20, 150 | nameservers: "10.0.0.1, 8.8.8.8", 151 | gateway: "10.0.0.1", 152 | optional: true, 153 | }; 154 | 155 | defaults.network.ethernets.eth0 = eth0; 156 | 157 | var expected_ethernets = { 158 | eth0: { ...eth0 }, 159 | eth1: { 160 | dhcp4: false, 161 | dhcp6: false, 162 | addresses: ["10.0.0.1/20"], 163 | routes: [{ to: "default", via: eth1_config.gateway }], 164 | nameservers: { 165 | addresses: ["10.0.0.1", "8.8.8.8"], 166 | }, 167 | optional: true, 168 | }, 169 | }; 170 | expect(templates.generate(defaults, eth1_config).network.ethernets).to.eql( 171 | expected_ethernets, 172 | ); 173 | }); 174 | 175 | it("it should set interface to dynmic ip", () => { 176 | var config = { 177 | interface: "eth1", 178 | dhcp: true, 179 | optional: true, 180 | }; 181 | 182 | var expected_ethernets = { 183 | eth1: { 184 | dhcp4: true, 185 | dhcp6: true, 186 | optional: true, 187 | "dhcp-identifier": "mac", 188 | }, 189 | }; 190 | expect(templates.generate(defaults, config).network.ethernets).to.eql( 191 | expected_ethernets, 192 | ); 193 | }); 194 | 195 | describe("VLAN support", () => { 196 | it("should create vlan with dynamic address", () => { 197 | var config = { 198 | ifname: "eth0.0", 199 | interface: "eth0", 200 | vlanid: 0, 201 | dhcp: true, 202 | optional: true, 203 | }; 204 | var expected_vlans = { 205 | "eth0.0": { 206 | id: 0, 207 | link: "eth0", 208 | dhcp4: true, 209 | dhcp6: true, 210 | optional: true, 211 | "dhcp-identifier": "mac", 212 | }, 213 | }; 214 | expect(templates.generate(defaults, config).network.vlans).to.eql( 215 | expected_vlans, 216 | ); 217 | }); 218 | 219 | it("should create vlan interface with no gateway and nameservers", () => { 220 | var config = { 221 | ifname: "eth0.0", 222 | interface: "eth0", 223 | vlanid: 0, 224 | ip_address: "20.0.0.1", 225 | prefix: 20, 226 | }; 227 | var expected_vlans = { 228 | "eth0.0": { 229 | id: 0, 230 | link: "eth0", 231 | dhcp4: false, 232 | dhcp6: false, 233 | addresses: ["20.0.0.1/20"], 234 | }, 235 | }; 236 | expect(templates.generate(defaults, config).network.vlans).to.eql( 237 | expected_vlans, 238 | ); 239 | }); 240 | 241 | it("should create vlan interface with gateway and no nameservers", () => { 242 | var config = { 243 | ifname: "eth0.10", 244 | interface: "eth0", 245 | vlanid: 10, 246 | ip_address: "20.0.0.1", 247 | prefix: 20, 248 | gateway: "20.0.0.1", 249 | }; 250 | var expected_vlans = { 251 | "eth0.10": { 252 | id: 10, 253 | link: "eth0", 254 | dhcp4: false, 255 | dhcp6: false, 256 | addresses: ["20.0.0.1/20"], 257 | routes: [{ to: "default", via: "20.0.0.1" }], 258 | }, 259 | }; 260 | expect(templates.generate(defaults, config).network.vlans).to.eql( 261 | expected_vlans, 262 | ); 263 | }); 264 | 265 | it("should create vlan interface with gateway nameservers", () => { 266 | var config = { 267 | ifname: "eth0.x", 268 | interface: "eth0", 269 | vlanid: 10, 270 | ip_address: "20.0.0.1", 271 | prefix: 20, 272 | gateway: "20.0.0.1", 273 | nameservers: ["1.1.1.1"], 274 | }; 275 | var expected_vlans = { 276 | "eth0.x": { 277 | id: 10, 278 | link: "eth0", 279 | dhcp4: false, 280 | dhcp6: false, 281 | addresses: ["20.0.0.1/20"], 282 | routes: [{ to: "default", via: "20.0.0.1" }], 283 | nameservers: { 284 | addresses: ["1.1.1.1"], 285 | }, 286 | }, 287 | }; 288 | expect(templates.generate(defaults, config).network.vlans).to.eql( 289 | expected_vlans, 290 | ); 291 | }); 292 | 293 | it("should create vlan interface with default options", () => { 294 | var config = { 295 | ifname: "eth0.0", 296 | interface: "eth0", 297 | vlanid: 0, 298 | optional: true, 299 | dhcp: false, 300 | }; 301 | var expected_vlans = { 302 | "eth0.0": { 303 | id: 0, 304 | link: "eth0", 305 | dhcp4: false, 306 | dhcp6: false, 307 | optional: true, 308 | }, 309 | }; 310 | expect(templates.generate(defaults, config).network.vlans).to.eql( 311 | expected_vlans, 312 | ); 313 | }); 314 | }); 315 | 316 | describe("bridged support", () => { 317 | it("should create bridge interfaces", () => { 318 | var config = { 319 | interface: "br0", 320 | ip_address: "20.0.0.1", 321 | prefix: 20, 322 | gateway: "20.0.0.1", 323 | bridge_ports: ["eth0"], 324 | bridge_opts: { parameters: { stp: true } }, 325 | optional: true, 326 | }; 327 | var expected_bridges = { 328 | br0: { 329 | dhcp4: false, 330 | dhcp6: false, 331 | addresses: ["20.0.0.1/20"], 332 | routes: [{ to: "default", via: "20.0.0.1" }], 333 | interfaces: ["eth0"], 334 | parameters: { 335 | stp: true, 336 | }, 337 | optional: true, 338 | }, 339 | }; 340 | var res = templates.generate(defaults, config); 341 | expect(res.network.ethernets).to.eql({ 342 | eth0: { dhcp4: false, dhcp6: false, optional: true }, 343 | }); 344 | expect(res.network.bridges).to.eql(expected_bridges); 345 | }); 346 | 347 | it("should create bridge interfaces with ethernets", () => { 348 | var config = { 349 | interface: "br0", 350 | ip_address: "20.0.0.1", 351 | prefix: 20, 352 | gateway: "20.0.0.1", 353 | bridge_ports: ["eth0"], 354 | bridge_opts: { 355 | parameters: { stp: false, forward_delay: 0 }, 356 | }, 357 | }; 358 | defaults.network.ethernets = { 359 | eth0: { 360 | dhcp4: true, 361 | match: { 362 | macaddress: "xxx", 363 | }, 364 | }, 365 | }; 366 | var expected_bridges = { 367 | br0: { 368 | dhcp4: false, 369 | dhcp6: false, 370 | addresses: ["20.0.0.1/20"], 371 | routes: [{ to: "default", via: "20.0.0.1" }], 372 | interfaces: ["eth0"], 373 | parameters: { stp: false, forward_delay: 0 }, 374 | }, 375 | }; 376 | var res = templates.generate(defaults, config); 377 | 378 | // expect(res.network.ethernets.eth0).to.be.undefined; 379 | expect(res.network.ethernets["eth0"]).to.eql({ 380 | dhcp4: false, 381 | dhcp6: false, 382 | optional: true, 383 | match: { 384 | macaddress: "xxx", 385 | }, 386 | }); 387 | expect(res.network.bridges).to.eql(expected_bridges); 388 | }); 389 | 390 | it("should create bridge interfaces with vlan", () => { 391 | var config = { 392 | interface: "br0", 393 | ip_address: "20.0.0.1", 394 | prefix: 20, 395 | gateway: "20.0.0.1", 396 | bridge_ports: ["eth0.10"], 397 | bridge_opts: { 398 | parameters: { stp: false, forward_delay: 0 }, 399 | }, 400 | }; 401 | defaults.network.vlans = { 402 | "eth0.10": { 403 | id: 10, 404 | link: "eth0", 405 | dhcp4: true, 406 | }, 407 | }; 408 | var expected_bridges = { 409 | br0: { 410 | dhcp4: false, 411 | dhcp6: false, 412 | addresses: ["20.0.0.1/20"], 413 | routes: [{ to: "default", via: "20.0.0.1" }], 414 | interfaces: ["eth0.10"], 415 | parameters: { stp: false, forward_delay: 0 }, 416 | }, 417 | }; 418 | var res = templates.generate(defaults, config); 419 | 420 | expect(res.network.ethernets.eth0).to.be.undefined; 421 | expect(res.network.vlans["eth0.10"]).to.eql({ 422 | id: 10, 423 | link: "eth0", 424 | dhcp4: false, 425 | dhcp6: false, 426 | optional: true, 427 | }); 428 | expect(res.network.bridges).to.eql(expected_bridges); 429 | }); 430 | }); 431 | 432 | describe("ppp exclusion", () => { 433 | it("should not add ppp interface to ethernets", () => { 434 | var config = { 435 | physical_interface: "eth1", 436 | provider: "dsl-provider", 437 | ppp: true, 438 | }; 439 | expect(templates.generate(defaults, config).network.ethernets).to.empty; 440 | }); 441 | }); 442 | }); 443 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.7.0": 6 | version "1.8.6" 7 | resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" 8 | integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== 9 | dependencies: 10 | type-detect "4.0.8" 11 | 12 | "@sinonjs/formatio@^3.2.1": 13 | version "3.2.2" 14 | resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" 15 | integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== 16 | dependencies: 17 | "@sinonjs/commons" "^1" 18 | "@sinonjs/samsam" "^3.1.0" 19 | 20 | "@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.3": 21 | version "3.3.3" 22 | resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" 23 | integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== 24 | dependencies: 25 | "@sinonjs/commons" "^1.3.0" 26 | array-from "^2.1.1" 27 | lodash "^4.17.15" 28 | 29 | "@sinonjs/text-encoding@^0.7.1": 30 | version "0.7.3" 31 | resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz#282046f03e886e352b2d5f5da5eb755e01457f3f" 32 | integrity sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA== 33 | 34 | ansi-colors@^4.1.3: 35 | version "4.1.3" 36 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" 37 | integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== 38 | 39 | ansi-regex@^5.0.1: 40 | version "5.0.1" 41 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 42 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 43 | 44 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 45 | version "4.3.0" 46 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 47 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 48 | dependencies: 49 | color-convert "^2.0.1" 50 | 51 | anymatch@~3.1.2: 52 | version "3.1.3" 53 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 54 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 55 | dependencies: 56 | normalize-path "^3.0.0" 57 | picomatch "^2.0.4" 58 | 59 | argparse@^1.0.7: 60 | version "1.0.10" 61 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 62 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 63 | dependencies: 64 | sprintf-js "~1.0.2" 65 | 66 | argparse@^2.0.1: 67 | version "2.0.1" 68 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 69 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 70 | 71 | array-from@^2.1.1: 72 | version "2.1.1" 73 | resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" 74 | integrity sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg== 75 | 76 | assertion-error@^1.1.0: 77 | version "1.1.0" 78 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 79 | integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== 80 | 81 | assign-deep@^1.0.1: 82 | version "1.0.1" 83 | resolved "https://registry.yarnpkg.com/assign-deep/-/assign-deep-1.0.1.tgz#b6d21d74e2f28bf6592e4c0c541bed6ab59c5f27" 84 | integrity sha512-CSXAX79mibneEYfqLT5FEmkqR5WXF+xDRjgQQuVf6wSCXCYU8/vHttPidNar7wJ5BFmKAo8Wei0rCtzb+M/yeA== 85 | dependencies: 86 | assign-symbols "^2.0.2" 87 | 88 | assign-symbols@^2.0.2: 89 | version "2.0.2" 90 | resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-2.0.2.tgz#0fb9191dd9d617042746ecfc354f3a3d768a0c98" 91 | integrity sha512-9sBQUQZMKFKcO/C3Bo6Rx4CQany0R0UeVcefNGRRdW2vbmaMOhV1sbmlXcQLcD56juLXbSGTBm0GGuvmrAF8pA== 92 | 93 | balanced-match@^1.0.0: 94 | version "1.0.2" 95 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 96 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 97 | 98 | binary-extensions@^2.0.0: 99 | version "2.3.0" 100 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" 101 | integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== 102 | 103 | brace-expansion@^2.0.1: 104 | version "2.0.2" 105 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" 106 | integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== 107 | dependencies: 108 | balanced-match "^1.0.0" 109 | 110 | braces@~3.0.2: 111 | version "3.0.3" 112 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 113 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== 114 | dependencies: 115 | fill-range "^7.1.1" 116 | 117 | browser-stdout@^1.3.1: 118 | version "1.3.1" 119 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 120 | integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== 121 | 122 | camelcase@^6.0.0: 123 | version "6.3.0" 124 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" 125 | integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== 126 | 127 | chai@^4.2.0: 128 | version "4.5.0" 129 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" 130 | integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== 131 | dependencies: 132 | assertion-error "^1.1.0" 133 | check-error "^1.0.3" 134 | deep-eql "^4.1.3" 135 | get-func-name "^2.0.2" 136 | loupe "^2.3.6" 137 | pathval "^1.1.1" 138 | type-detect "^4.1.0" 139 | 140 | chalk@^4.1.0: 141 | version "4.1.2" 142 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 143 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 144 | dependencies: 145 | ansi-styles "^4.1.0" 146 | supports-color "^7.1.0" 147 | 148 | check-error@^1.0.3: 149 | version "1.0.3" 150 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" 151 | integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== 152 | dependencies: 153 | get-func-name "^2.0.2" 154 | 155 | chokidar@^3.5.3: 156 | version "3.6.0" 157 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" 158 | integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== 159 | dependencies: 160 | anymatch "~3.1.2" 161 | braces "~3.0.2" 162 | glob-parent "~5.1.2" 163 | is-binary-path "~2.1.0" 164 | is-glob "~4.0.1" 165 | normalize-path "~3.0.0" 166 | readdirp "~3.6.0" 167 | optionalDependencies: 168 | fsevents "~2.3.2" 169 | 170 | cliui@^7.0.2: 171 | version "7.0.4" 172 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" 173 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== 174 | dependencies: 175 | string-width "^4.2.0" 176 | strip-ansi "^6.0.0" 177 | wrap-ansi "^7.0.0" 178 | 179 | color-convert@^2.0.1: 180 | version "2.0.1" 181 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 182 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 183 | dependencies: 184 | color-name "~1.1.4" 185 | 186 | color-name@~1.1.4: 187 | version "1.1.4" 188 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 189 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 190 | 191 | debug@^4.3.5: 192 | version "4.4.1" 193 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" 194 | integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== 195 | dependencies: 196 | ms "^2.1.3" 197 | 198 | decamelize@^4.0.0: 199 | version "4.0.0" 200 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" 201 | integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== 202 | 203 | deep-eql@^4.1.3: 204 | version "4.1.4" 205 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7" 206 | integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg== 207 | dependencies: 208 | type-detect "^4.0.0" 209 | 210 | diff@^3.5.0: 211 | version "3.5.0" 212 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 213 | integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== 214 | 215 | diff@^5.2.0: 216 | version "5.2.0" 217 | resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" 218 | integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== 219 | 220 | emoji-regex@^8.0.0: 221 | version "8.0.0" 222 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 223 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 224 | 225 | escalade@^3.1.1: 226 | version "3.2.0" 227 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" 228 | integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== 229 | 230 | escape-string-regexp@^4.0.0: 231 | version "4.0.0" 232 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 233 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 234 | 235 | esprima@^4.0.0: 236 | version "4.0.1" 237 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 238 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 239 | 240 | fill-keys@^1.0.2: 241 | version "1.0.2" 242 | resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" 243 | integrity sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA== 244 | dependencies: 245 | is-object "~1.0.1" 246 | merge-descriptors "~1.0.0" 247 | 248 | fill-range@^7.1.1: 249 | version "7.1.1" 250 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 251 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== 252 | dependencies: 253 | to-regex-range "^5.0.1" 254 | 255 | find-up@^5.0.0: 256 | version "5.0.0" 257 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 258 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 259 | dependencies: 260 | locate-path "^6.0.0" 261 | path-exists "^4.0.0" 262 | 263 | flat@^5.0.2: 264 | version "5.0.2" 265 | resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" 266 | integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== 267 | 268 | fs.realpath@^1.0.0: 269 | version "1.0.0" 270 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 271 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 272 | 273 | fsevents@~2.3.2: 274 | version "2.3.3" 275 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" 276 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== 277 | 278 | function-bind@^1.1.2: 279 | version "1.1.2" 280 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" 281 | integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 282 | 283 | get-caller-file@^2.0.5: 284 | version "2.0.5" 285 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 286 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 287 | 288 | get-func-name@^2.0.1, get-func-name@^2.0.2: 289 | version "2.0.2" 290 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" 291 | integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== 292 | 293 | glob-parent@~5.1.2: 294 | version "5.1.2" 295 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 296 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 297 | dependencies: 298 | is-glob "^4.0.1" 299 | 300 | glob@^8.1.0: 301 | version "8.1.0" 302 | resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" 303 | integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== 304 | dependencies: 305 | fs.realpath "^1.0.0" 306 | inflight "^1.0.4" 307 | inherits "2" 308 | minimatch "^5.0.1" 309 | once "^1.3.0" 310 | 311 | has-flag@^3.0.0: 312 | version "3.0.0" 313 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 314 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== 315 | 316 | has-flag@^4.0.0: 317 | version "4.0.0" 318 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 319 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 320 | 321 | hasown@^2.0.2: 322 | version "2.0.2" 323 | resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" 324 | integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== 325 | dependencies: 326 | function-bind "^1.1.2" 327 | 328 | he@^1.2.0: 329 | version "1.2.0" 330 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 331 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 332 | 333 | inflight@^1.0.4: 334 | version "1.0.6" 335 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 336 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 337 | dependencies: 338 | once "^1.3.0" 339 | wrappy "1" 340 | 341 | inherits@2: 342 | version "2.0.4" 343 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 344 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 345 | 346 | is-binary-path@~2.1.0: 347 | version "2.1.0" 348 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 349 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 350 | dependencies: 351 | binary-extensions "^2.0.0" 352 | 353 | is-core-module@^2.16.0: 354 | version "2.16.1" 355 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" 356 | integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== 357 | dependencies: 358 | hasown "^2.0.2" 359 | 360 | is-extglob@^2.1.1: 361 | version "2.1.1" 362 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 363 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 364 | 365 | is-fullwidth-code-point@^3.0.0: 366 | version "3.0.0" 367 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 368 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 369 | 370 | is-glob@^4.0.1, is-glob@~4.0.1: 371 | version "4.0.3" 372 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 373 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 374 | dependencies: 375 | is-extglob "^2.1.1" 376 | 377 | is-number@^7.0.0: 378 | version "7.0.0" 379 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 380 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 381 | 382 | is-object@~1.0.1: 383 | version "1.0.2" 384 | resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" 385 | integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== 386 | 387 | is-plain-obj@^2.1.0: 388 | version "2.1.0" 389 | resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" 390 | integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== 391 | 392 | is-unicode-supported@^0.1.0: 393 | version "0.1.0" 394 | resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" 395 | integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== 396 | 397 | isarray@0.0.1: 398 | version "0.0.1" 399 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 400 | integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== 401 | 402 | js-yaml@^3.13.1: 403 | version "3.14.1" 404 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" 405 | integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== 406 | dependencies: 407 | argparse "^1.0.7" 408 | esprima "^4.0.0" 409 | 410 | js-yaml@^4.1.0: 411 | version "4.1.0" 412 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 413 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 414 | dependencies: 415 | argparse "^2.0.1" 416 | 417 | just-extend@^4.0.2: 418 | version "4.2.1" 419 | resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" 420 | integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== 421 | 422 | locate-path@^6.0.0: 423 | version "6.0.0" 424 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" 425 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 426 | dependencies: 427 | p-locate "^5.0.0" 428 | 429 | lodash@^4.17.15: 430 | version "4.17.21" 431 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 432 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 433 | 434 | log-symbols@^4.1.0: 435 | version "4.1.0" 436 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" 437 | integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== 438 | dependencies: 439 | chalk "^4.1.0" 440 | is-unicode-supported "^0.1.0" 441 | 442 | lolex@^4.2.0: 443 | version "4.2.0" 444 | resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" 445 | integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== 446 | 447 | lolex@^5.0.1: 448 | version "5.1.2" 449 | resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" 450 | integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== 451 | dependencies: 452 | "@sinonjs/commons" "^1.7.0" 453 | 454 | loupe@^2.3.6: 455 | version "2.3.7" 456 | resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" 457 | integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== 458 | dependencies: 459 | get-func-name "^2.0.1" 460 | 461 | make-dir@^3.0.0: 462 | version "3.1.0" 463 | resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" 464 | integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== 465 | dependencies: 466 | semver "^6.0.0" 467 | 468 | merge-descriptors@~1.0.0: 469 | version "1.0.3" 470 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" 471 | integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== 472 | 473 | minimatch@^5.0.1, minimatch@^5.1.6: 474 | version "5.1.6" 475 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" 476 | integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== 477 | dependencies: 478 | brace-expansion "^2.0.1" 479 | 480 | mocha@^10.1.0: 481 | version "10.8.2" 482 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.8.2.tgz#8d8342d016ed411b12a429eb731b825f961afb96" 483 | integrity sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg== 484 | dependencies: 485 | ansi-colors "^4.1.3" 486 | browser-stdout "^1.3.1" 487 | chokidar "^3.5.3" 488 | debug "^4.3.5" 489 | diff "^5.2.0" 490 | escape-string-regexp "^4.0.0" 491 | find-up "^5.0.0" 492 | glob "^8.1.0" 493 | he "^1.2.0" 494 | js-yaml "^4.1.0" 495 | log-symbols "^4.1.0" 496 | minimatch "^5.1.6" 497 | ms "^2.1.3" 498 | serialize-javascript "^6.0.2" 499 | strip-json-comments "^3.1.1" 500 | supports-color "^8.1.1" 501 | workerpool "^6.5.1" 502 | yargs "^16.2.0" 503 | yargs-parser "^20.2.9" 504 | yargs-unparser "^2.0.0" 505 | 506 | module-not-found-error@^1.0.1: 507 | version "1.0.1" 508 | resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" 509 | integrity sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g== 510 | 511 | ms@^2.1.3: 512 | version "2.1.3" 513 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 514 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 515 | 516 | netmask@^2.0.2: 517 | version "2.0.2" 518 | resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" 519 | integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== 520 | 521 | nise@^1.5.2: 522 | version "1.5.3" 523 | resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" 524 | integrity sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ== 525 | dependencies: 526 | "@sinonjs/formatio" "^3.2.1" 527 | "@sinonjs/text-encoding" "^0.7.1" 528 | just-extend "^4.0.2" 529 | lolex "^5.0.1" 530 | path-to-regexp "^1.7.0" 531 | 532 | normalize-path@^3.0.0, normalize-path@~3.0.0: 533 | version "3.0.0" 534 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 535 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 536 | 537 | once@^1.3.0: 538 | version "1.4.0" 539 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 540 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 541 | dependencies: 542 | wrappy "1" 543 | 544 | p-limit@^3.0.2: 545 | version "3.1.0" 546 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 547 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 548 | dependencies: 549 | yocto-queue "^0.1.0" 550 | 551 | p-locate@^5.0.0: 552 | version "5.0.0" 553 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" 554 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 555 | dependencies: 556 | p-limit "^3.0.2" 557 | 558 | path-exists@^4.0.0: 559 | version "4.0.0" 560 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 561 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 562 | 563 | path-parse@^1.0.7: 564 | version "1.0.7" 565 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 566 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 567 | 568 | path-to-regexp@^1.7.0: 569 | version "1.9.0" 570 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24" 571 | integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g== 572 | dependencies: 573 | isarray "0.0.1" 574 | 575 | pathval@^1.1.1: 576 | version "1.1.1" 577 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" 578 | integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== 579 | 580 | picomatch@^2.0.4, picomatch@^2.2.1: 581 | version "2.3.1" 582 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 583 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 584 | 585 | proxyquire@^2.1.3: 586 | version "2.1.3" 587 | resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" 588 | integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg== 589 | dependencies: 590 | fill-keys "^1.0.2" 591 | module-not-found-error "^1.0.1" 592 | resolve "^1.11.1" 593 | 594 | randombytes@^2.1.0: 595 | version "2.1.0" 596 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" 597 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== 598 | dependencies: 599 | safe-buffer "^5.1.0" 600 | 601 | readdirp@~3.6.0: 602 | version "3.6.0" 603 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 604 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 605 | dependencies: 606 | picomatch "^2.2.1" 607 | 608 | require-directory@^2.1.1: 609 | version "2.1.1" 610 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 611 | integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== 612 | 613 | resolve@^1.11.1: 614 | version "1.22.10" 615 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" 616 | integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== 617 | dependencies: 618 | is-core-module "^2.16.0" 619 | path-parse "^1.0.7" 620 | supports-preserve-symlinks-flag "^1.0.0" 621 | 622 | safe-buffer@^5.1.0: 623 | version "5.2.1" 624 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 625 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 626 | 627 | semver@^6.0.0: 628 | version "6.3.1" 629 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" 630 | integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== 631 | 632 | serialize-javascript@^6.0.2: 633 | version "6.0.2" 634 | resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" 635 | integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== 636 | dependencies: 637 | randombytes "^2.1.0" 638 | 639 | sinon@^7.5.0: 640 | version "7.5.0" 641 | resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.5.0.tgz#e9488ea466070ea908fd44a3d6478fd4923c67ec" 642 | integrity sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q== 643 | dependencies: 644 | "@sinonjs/commons" "^1.4.0" 645 | "@sinonjs/formatio" "^3.2.1" 646 | "@sinonjs/samsam" "^3.3.3" 647 | diff "^3.5.0" 648 | lolex "^4.2.0" 649 | nise "^1.5.2" 650 | supports-color "^5.5.0" 651 | 652 | sprintf-js@~1.0.2: 653 | version "1.0.3" 654 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 655 | integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== 656 | 657 | string-width@^4.1.0, string-width@^4.2.0: 658 | version "4.2.3" 659 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 660 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 661 | dependencies: 662 | emoji-regex "^8.0.0" 663 | is-fullwidth-code-point "^3.0.0" 664 | strip-ansi "^6.0.1" 665 | 666 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 667 | version "6.0.1" 668 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 669 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 670 | dependencies: 671 | ansi-regex "^5.0.1" 672 | 673 | strip-json-comments@^3.1.1: 674 | version "3.1.1" 675 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 676 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 677 | 678 | supports-color@^5.5.0: 679 | version "5.5.0" 680 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 681 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 682 | dependencies: 683 | has-flag "^3.0.0" 684 | 685 | supports-color@^7.1.0: 686 | version "7.2.0" 687 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 688 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 689 | dependencies: 690 | has-flag "^4.0.0" 691 | 692 | supports-color@^8.1.1: 693 | version "8.1.1" 694 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" 695 | integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== 696 | dependencies: 697 | has-flag "^4.0.0" 698 | 699 | supports-preserve-symlinks-flag@^1.0.0: 700 | version "1.0.0" 701 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 702 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 703 | 704 | to-regex-range@^5.0.1: 705 | version "5.0.1" 706 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 707 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 708 | dependencies: 709 | is-number "^7.0.0" 710 | 711 | type-detect@4.0.8: 712 | version "4.0.8" 713 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 714 | integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== 715 | 716 | type-detect@^4.0.0, type-detect@^4.1.0: 717 | version "4.1.0" 718 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" 719 | integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== 720 | 721 | workerpool@^6.5.1: 722 | version "6.5.1" 723 | resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" 724 | integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== 725 | 726 | wrap-ansi@^7.0.0: 727 | version "7.0.0" 728 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 729 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 730 | dependencies: 731 | ansi-styles "^4.0.0" 732 | string-width "^4.1.0" 733 | strip-ansi "^6.0.0" 734 | 735 | wrappy@1: 736 | version "1.0.2" 737 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 738 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 739 | 740 | y18n@^5.0.5: 741 | version "5.0.8" 742 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" 743 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 744 | 745 | yargs-parser@^20.2.2, yargs-parser@^20.2.9: 746 | version "20.2.9" 747 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" 748 | integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== 749 | 750 | yargs-unparser@^2.0.0: 751 | version "2.0.0" 752 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" 753 | integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== 754 | dependencies: 755 | camelcase "^6.0.0" 756 | decamelize "^4.0.0" 757 | flat "^5.0.2" 758 | is-plain-obj "^2.1.0" 759 | 760 | yargs@^16.2.0: 761 | version "16.2.0" 762 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" 763 | integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== 764 | dependencies: 765 | cliui "^7.0.2" 766 | escalade "^3.1.1" 767 | get-caller-file "^2.0.5" 768 | require-directory "^2.1.1" 769 | string-width "^4.2.0" 770 | y18n "^5.0.5" 771 | yargs-parser "^20.2.2" 772 | 773 | yocto-queue@^0.1.0: 774 | version "0.1.0" 775 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 776 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 777 | --------------------------------------------------------------------------------