Merge branch 'add-electron-app' into service-testing
commit
ec6b3a068b
|
|
@ -15,19 +15,19 @@
|
|||
"productName": "ElectronReact",
|
||||
"description": "Electron application boilerplate based on React, React Router, Webpack, React Fast Refresh for rapid application development",
|
||||
"scripts": {
|
||||
"postinstall": "node -r @babel/register .erb/scripts/CheckNativeDep.js && electron-builder install-app-deps && yarn cross-env NODE_ENV=development webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.babel.js && opencollective-postinstall && yarn-deduplicate yarn.lock",
|
||||
"build": "concurrently \"yarn build:main\" \"yarn build:renderer\"",
|
||||
"build:main": "cross-env NODE_ENV=production webpack --config ./.erb/configs/webpack.config.main.prod.babel.js",
|
||||
"build:renderer": "cross-env NODE_ENV=production webpack --config ./.erb/configs/webpack.config.renderer.prod.babel.js",
|
||||
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir src",
|
||||
"lint": "cross-env NODE_ENV=development eslint . --cache --ext .js,.jsx,.ts,.tsx",
|
||||
"package": "rm -rf src/dist && yarn build && electron-builder build --publish never",
|
||||
"postinstall": "node -r @babel/register .erb/scripts/CheckNativeDep.js && electron-builder install-app-deps && yarn cross-env NODE_ENV=development webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.babel.js && opencollective-postinstall && yarn-deduplicate yarn.lock",
|
||||
"start": "node -r @babel/register ./.erb/scripts/CheckPortInUse.js && cross-env yarn start:renderer",
|
||||
"start:main": "cross-env NODE_ENV=development electron -r ./.erb/scripts/BabelRegister ./src/main.dev.ts",
|
||||
"start:renderer": "cross-env NODE_ENV=development webpack serve --config ./.erb/configs/webpack.config.renderer.dev.babel.js",
|
||||
"test": "jest",
|
||||
"relay": "supervisor -w relay,src -n exit relay/index.js",
|
||||
"relay:service": "supervisor -- relay/service",
|
||||
"relay": "supervisor -w relay,src -n exit relay/index.mjs",
|
||||
"relay:service": "supervisor -- relay/service.mjs",
|
||||
"sloc": "find lib -type f | xargs wc -l"
|
||||
},
|
||||
"build": {
|
||||
|
|
@ -260,6 +260,7 @@
|
|||
"md5": "^2.3.0",
|
||||
"menubar": "^9.0.3",
|
||||
"nat-upnp": "^1.1.1",
|
||||
"nedb": "^1.8.0",
|
||||
"node-ipc": "^9.1.4",
|
||||
"node-rsa": "^1.1.1",
|
||||
"printable-characters": "^1.0.42",
|
||||
|
|
|
|||
|
|
@ -1,21 +1,26 @@
|
|||
// process.env.DEBUG = 'xyz:valnet:*';
|
||||
'use strict';
|
||||
|
||||
import { title } from '../src/lib/title.js';
|
||||
import { Identity } from '../src/lib/Identity.js';
|
||||
import { config } from '../src/lib/config/index.js';
|
||||
import appdata from '../src/lib/appdata.js';
|
||||
import Node from '../src/lib/node.js';
|
||||
|
||||
import Signale from 'signale';
|
||||
import { ensureDirSync } from 'fs-extra';
|
||||
import express from 'express';
|
||||
|
||||
(async () => {
|
||||
const { title } = require('../src/lib/title');
|
||||
const log = require('signale').scope('RLAY');
|
||||
const { Identity } = require('../src/lib/Identity');
|
||||
title('relay', false);
|
||||
const Node = require('../src/lib/node');
|
||||
const { config } = require('../src/lib/config');
|
||||
const { ensureDirSync } = require('fs-extra');
|
||||
const appdata = require('../src/lib/appdata');
|
||||
|
||||
ensureDirSync(`${appdata}/valnet/relay`);
|
||||
const log = Signale.scope('RLAY');
|
||||
const node = new Node();
|
||||
|
||||
(async () => {
|
||||
|
||||
// ==================================== [EXPRESS]
|
||||
const express = require('express');
|
||||
|
||||
const app = express();
|
||||
ensureDirSync(`${appdata}/valnet/relay`);
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.end(`
|
||||
|
|
@ -1,21 +1,24 @@
|
|||
import Signale from 'signale';
|
||||
import { execSync, spawn } from 'child_process';
|
||||
import Datastore from 'nedb';
|
||||
import { config } from '../src/lib/config/index.js';
|
||||
import express from 'express';
|
||||
|
||||
(async () => {
|
||||
const log = require('signale').scope('SRVC');
|
||||
const { execSync, spawn } = require('child_process');
|
||||
|
||||
const log = Signale.scope('SRVC');
|
||||
const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
|
||||
let proc;
|
||||
const Datastore = require('nedb');
|
||||
const logs = new Datastore({
|
||||
filename: 'svc.log',
|
||||
autoload: true
|
||||
});
|
||||
const { config } = require('../package.json');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
try {
|
||||
appendLogs('yarn', execSync(`yarn`));
|
||||
} catch (e) {
|
||||
appendLogs('failed to yarn install...')
|
||||
logp('failed to yarn install...')
|
||||
}
|
||||
|
||||
logp('==================================');
|
||||
|
|
@ -47,7 +50,7 @@ setInterval(function update() {
|
|||
}, 5000);
|
||||
|
||||
(function keepAlive() {
|
||||
proc = spawn('node', ['relay'], {
|
||||
proc = spawn('node', ['./relay/index.mjs'], {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
|
||||
17
src/App.tsx
17
src/App.tsx
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
|
||||
import icon from '../assets/icon.svg';
|
||||
import './App.global.css';
|
||||
import { IpcClient } from './lib/ipc';
|
||||
import { IpcClient } from './lib/ipc.js';
|
||||
|
||||
|
||||
class Hello extends React.Component {
|
||||
api = new IpcClient('valnet');
|
||||
|
|
@ -15,6 +16,11 @@ class Hello extends React.Component {
|
|||
console.log(await this.api.getClients())
|
||||
}
|
||||
|
||||
async killApp() {
|
||||
// alert(this);
|
||||
await this.api.kill(1);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -24,20 +30,19 @@ class Hello extends React.Component {
|
|||
<h1>electron-react-boilerplate</h1>
|
||||
<div className="Hello">
|
||||
<a
|
||||
href="https://electron-react-boilerplate.js.org/"
|
||||
target="_blank"
|
||||
href="#"
|
||||
rel="noreferrer"
|
||||
onClick={this.killApp.bind(this)}
|
||||
>
|
||||
<button type="button">
|
||||
<span role="img" aria-label="books">
|
||||
📚
|
||||
</span>
|
||||
Read our docs
|
||||
Kill App
|
||||
</button>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/sponsors/electron-react-boilerplate"
|
||||
target="_blank"
|
||||
target="#"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<button type="button">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ module.exports.StreamJsonParser = class StreamJsonParser extends EventEmitter {
|
|||
buffer = '';
|
||||
|
||||
data(evt) {
|
||||
console.log(evt)
|
||||
// toString it, in case its a buffer!
|
||||
this.buffer += evt.toString();
|
||||
this.processBuffer();
|
||||
|
|
@ -15,8 +14,6 @@ module.exports.StreamJsonParser = class StreamJsonParser extends EventEmitter {
|
|||
const parts = this.buffer.split(/(\x02[^\x02\x03]*\x03)/g);
|
||||
this.buffer = '';
|
||||
|
||||
console.log('process', this.buffer);
|
||||
|
||||
for(const message of parts) {
|
||||
if(message.endsWith('\x03')) {
|
||||
const obj = JSON.parse(message.substr(1, message.length - 2));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
"ports": {
|
||||
"relay": 5600,
|
||||
"relayEnd": 5699,
|
||||
"http": 5700,
|
||||
"service": 5000
|
||||
},
|
||||
"endpoints": [
|
||||
"valnet.xyz:5500",
|
||||
"35.196.210.135:5600"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
const { readFileSync, writeFileSync, existsSync } = require('fs');
|
||||
const { ensureDirSync } = require('fs-extra');
|
||||
const { config } = require('../../package.json');
|
||||
const config = require('./defaults.js');
|
||||
const deepmerge = require('deepmerge');
|
||||
|
||||
const appdata = require('./appdata');
|
||||
const appdata = require('../appdata');
|
||||
ensureDirSync(`${appdata}/valnet/relay`);
|
||||
const filepath = `${appdata}/valnet/relay/config.json`;
|
||||
|
||||
|
|
@ -28,8 +28,6 @@ class IpcServer extends EventEmitter {
|
|||
ipc.server.on('req', (evt, incommingSocket) => {
|
||||
if (incommingSocket !== socket) return;
|
||||
|
||||
console.log('req -', evt);
|
||||
|
||||
const [
|
||||
name,
|
||||
...args
|
||||
|
|
@ -82,12 +80,8 @@ function IpcClient(name) {
|
|||
return this.proxy;
|
||||
|
||||
}
|
||||
if(!!module.exports) {
|
||||
module.exports.IpcServer = IpcServer;
|
||||
module.exports.IpcClient = IpcClient;
|
||||
}
|
||||
|
||||
export {
|
||||
module.exports = {
|
||||
IpcServer,
|
||||
IpcClient
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ const EventEmitter = require('events')
|
|||
const stp = require('./STP');
|
||||
const upnp = require('./upnp');
|
||||
const md5 = require('md5');
|
||||
const pkg = require('./../package.json');
|
||||
const { config, write } = require('./config.js');
|
||||
const { config, write } = require('./config/index.js');
|
||||
const log = require('signale').scope('NODE');
|
||||
const bonjour = require('bonjour')();
|
||||
const Gateway = require('./Gateway');
|
||||
|
|
@ -61,7 +60,7 @@ class Node extends EventEmitter {
|
|||
log.info('incomming connection from ' + connection.remoteName);
|
||||
});
|
||||
|
||||
log.info('advertising node on multicast...')
|
||||
log.info('advertising node on multicast...');
|
||||
this.multicastAd = bonjour.publish({
|
||||
name: this.name,
|
||||
type: 'stp',
|
||||
|
|
@ -76,6 +75,7 @@ class Node extends EventEmitter {
|
|||
|
||||
const ipcServer = new IpcServer('valnet');
|
||||
ipcServer.registerFunction(this.getClients.bind(this));
|
||||
ipcServer.registerFunction(this.kill.bind(this));
|
||||
|
||||
// log.success('Node successfully registered!');
|
||||
}
|
||||
|
|
@ -85,6 +85,10 @@ class Node extends EventEmitter {
|
|||
return this.clients;
|
||||
}
|
||||
|
||||
kill(code) {
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
async serviceUp(device) {
|
||||
this.multicastDevices.push(device);
|
||||
}
|
||||
|
|
@ -120,8 +124,6 @@ class Node extends EventEmitter {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(mappings, this.hash);
|
||||
}
|
||||
|
||||
static get Node() {
|
||||
|
|
|
|||
163
upnp.js
163
upnp.js
|
|
@ -1,163 +0,0 @@
|
|||
/* node UPNP port forwarding PoC
|
||||
This is a simple way to forward ports on NAT routers with UPNP.
|
||||
This is a not-for-production hack that I found useful when testing apps
|
||||
on my home network behind ny NAT router.
|
||||
-satori / edited by smolleyes for freebox v6
|
||||
usage: (install/clone node-ip from https://github.com/indutny/node-ip)
|
||||
================================================================================
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
var url = require("url");
|
||||
var http = require("http");
|
||||
var dgram = require("dgram");
|
||||
var Buffer = require("buffer").Buffer;
|
||||
|
||||
// some const strings - dont change
|
||||
const SSDP_PORT = 1901;
|
||||
const bcast = "239.255.255.250";
|
||||
const ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
|
||||
const req = "M-SEARCH * HTTP/1.1\r\nHost:239.255.255.250:1900\r\n\
|
||||
ST:"+ST+"\r\nMan:\"ssdp:discover\"\r\nMX:3\r\n\r\n";
|
||||
const WANIP = "urn:schemas-upnp-org:service:WANIPConnection:1";
|
||||
const OK = "HTTP/1.1 200 OK";
|
||||
const SOAP_ENV_PRE = "<?xml version=\"1.0\"?>\n<s:Envelope \
|
||||
xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" \
|
||||
s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>\n";
|
||||
const SOAP_ENV_POST = "</s:Body>\n</s:Envelope>\n";
|
||||
|
||||
function searchGateway(timeout, callback) {
|
||||
|
||||
var self = this;
|
||||
var reqbuf = new Buffer(req, "ascii");
|
||||
var socket = new dgram.Socket('udp4');
|
||||
var clients = {};
|
||||
var t;
|
||||
|
||||
if (timeout) {
|
||||
t = setTimeout(function() {
|
||||
onerror(new Error("searchGateway() timed out"));
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
var onlistening = function() {
|
||||
|
||||
socket.setBroadcast(socket.fd, true);
|
||||
// send a few packets just in case.
|
||||
socket.send(reqbuf, 0, reqbuf.length, SSDP_PORT,bcast);
|
||||
|
||||
}
|
||||
|
||||
var onmessage = function(message, rinfo) {
|
||||
|
||||
console.log('message?')
|
||||
message = message.toString();
|
||||
|
||||
if (message.substr(0, OK.length) !== OK ||
|
||||
!message.indexOf(ST) ||
|
||||
!message.indexOf("location:")) return;
|
||||
|
||||
var l = url.parse(message.match(/location:(.+?)\r\n/i)[1].trim());
|
||||
if (clients[l.href]) return;
|
||||
|
||||
var client = clients[l.href] = http.createClient(l.port, l.hostname);
|
||||
var request = client.request("GET", l.pathname, {"host": l.hostname});
|
||||
request.end();
|
||||
request.addListener('response', function (response) {
|
||||
if (response.statusCode !== 200) return;
|
||||
var resbuf = "";
|
||||
response.addListener('data', function (chunk) { resbuf += chunk });
|
||||
response.addListener("end", function() {
|
||||
resbuf = resbuf.substr(resbuf.indexOf(WANIP) + WANIP.length);
|
||||
var ipurl = resbuf.match(/<controlURL>(.+?)<\/controlURL>/i)[1].trim()
|
||||
socket.close();
|
||||
clearTimeout(t);
|
||||
callback(null, new Gateway(l.port, l.hostname, ipurl));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var onerror = function(err) {
|
||||
socket.close() ;
|
||||
clearTimeout(t);
|
||||
callback(err);
|
||||
}
|
||||
|
||||
var onclose = function() {
|
||||
socket.removeListener("listening", onlistening);
|
||||
socket.removeListener("message", onmessage);
|
||||
socket.removeListener("close", onclose);
|
||||
socket.removeListener("error", onerror);
|
||||
}
|
||||
|
||||
socket.addListener("listening", onlistening);
|
||||
socket.addListener("message", onmessage);
|
||||
socket.addListener("close", onclose);
|
||||
socket.addListener("error", onerror);
|
||||
|
||||
socket.bind(SSDP_PORT);
|
||||
|
||||
}
|
||||
exports.searchGateway = searchGateway;
|
||||
|
||||
function Gateway(port, host, path) {
|
||||
this.port = port;
|
||||
this.host = host;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
Gateway.prototype.getExternalIP = function(callback) {
|
||||
|
||||
var s =
|
||||
"<u:GetExternalIPAddress xmlns:u=\"" + WANIP + "\">\
|
||||
</u:GetExternalIPAddress>\n";
|
||||
|
||||
this._getSOAPResponse(s, "GetExternalIPAddress", function(err, xml) {
|
||||
if (err) callback(err);
|
||||
else callback(null,
|
||||
xml.match(/<NewExternalIPAddress>(.+?)<\/NewExternalIPAddress>/i)[1]);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Gateway.prototype.AddPortMapping = function(protocol
|
||||
, extPort
|
||||
, intPort
|
||||
, host
|
||||
, description
|
||||
, callback) {
|
||||
var s =
|
||||
"<u:AddPortMapping \
|
||||
xmlns:u=\""+WANIP+"\">\
|
||||
<NewRemoteHost></NewRemoteHost>\
|
||||
<NewExternalPort>"+extPort+"</NewExternalPort>\
|
||||
<NewProtocol>"+protocol+"</NewProtocol>\
|
||||
<NewInternalPort>"+intPort+"</NewInternalPort>\
|
||||
<NewInternalClient>"+host+"</NewInternalClient>\
|
||||
<NewEnabled>1</NewEnabled>\
|
||||
<NewPortMappingDescription>"+description+"</NewPortMappingDescription>\
|
||||
<NewLeaseDuration>0</NewLeaseDuration>\
|
||||
</u:AddPortMapping>";
|
||||
this._getSOAPResponse(s, "AddPortMapping", callback);
|
||||
}
|
||||
|
||||
Gateway.prototype._getSOAPResponse = function(soap, func, callback) {
|
||||
var s = [SOAP_ENV_PRE, soap, SOAP_ENV_POST].join("");
|
||||
var client = http.createClient(this.port, this.host);
|
||||
var hdrs = { "host" : this.host
|
||||
, "SOAPACTION" : "\"" + WANIP + "#" + func + "\""
|
||||
, "content-type" : "text/xml"
|
||||
, "content-length" : s.length };
|
||||
var request = client.request("POST", this.path, hdrs);
|
||||
request.end(s);
|
||||
request.addListener('response', function (response) {
|
||||
if (response.statusCode !== 200) {
|
||||
response.close();
|
||||
callback(new Error("Invalid SOAP action"));
|
||||
return;
|
||||
}
|
||||
var buf = "";
|
||||
response.addListener('data', function (chunk) { buf += chunk });
|
||||
response.addListener('end', function () { callback(null, buf) });
|
||||
});
|
||||
}
|
||||
42
yarn.lock
42
yarn.lock
|
|
@ -2496,6 +2496,11 @@ async-limiter@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
||||
|
||||
async@0.2.10:
|
||||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
|
||||
integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
|
||||
|
||||
async@0.9.x:
|
||||
version "0.9.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
|
||||
|
|
@ -2688,6 +2693,13 @@ binary-extensions@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
|
||||
integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
|
||||
|
||||
binary-search-tree@0.2.5:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/binary-search-tree/-/binary-search-tree-0.2.5.tgz#7dbb3b210fdca082450dad2334c304af39bdc784"
|
||||
integrity sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=
|
||||
dependencies:
|
||||
underscore "~1.4.4"
|
||||
|
||||
bindings@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
|
|
@ -7572,6 +7584,13 @@ levn@~0.3.0:
|
|||
prelude-ls "~1.1.2"
|
||||
type-check "~0.3.2"
|
||||
|
||||
lie@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
lie@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
|
||||
|
|
@ -7690,6 +7709,13 @@ loader-utils@^2.0.0:
|
|||
emojis-list "^3.0.0"
|
||||
json5 "^2.1.2"
|
||||
|
||||
localforage@^1.3.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.9.0.tgz#f3e4d32a8300b362b4634cc4e066d9d00d2f09d1"
|
||||
integrity sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==
|
||||
dependencies:
|
||||
lie "3.1.1"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
|
|
@ -8249,6 +8275,17 @@ nearley@^2.7.10:
|
|||
randexp "0.4.6"
|
||||
semver "^5.4.1"
|
||||
|
||||
nedb@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/nedb/-/nedb-1.8.0.tgz#0e3502cd82c004d5355a43c9e55577bd7bd91d88"
|
||||
integrity sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=
|
||||
dependencies:
|
||||
async "0.2.10"
|
||||
binary-search-tree "0.2.5"
|
||||
localforage "^1.3.0"
|
||||
mkdirp "~0.5.1"
|
||||
underscore "~1.4.4"
|
||||
|
||||
needle@^2.2.1:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.2.tgz#cf1a8fce382b5a280108bba90a14993c00e4010a"
|
||||
|
|
@ -11546,6 +11583,11 @@ typical@^5.0.0, typical@^5.2.0:
|
|||
resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066"
|
||||
integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==
|
||||
|
||||
underscore@~1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
|
||||
integrity sha1-YaajIBBiKvoHljvzJSA88SI51gQ=
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
||||
|
|
|
|||
Reference in New Issue