diff --git a/src/JavaScriptServices/.gitignore b/src/JavaScriptServices/.gitignore new file mode 100644 index 0000000000..79812ddba3 --- /dev/null +++ b/src/JavaScriptServices/.gitignore @@ -0,0 +1,31 @@ +[Oo]bj/ +[Bb]in/ +TestResults/ +.nuget/ +*.sln.ide/ +_ReSharper.*/ +packages/ +artifacts/ +PublishProfiles/ +*.user +*.suo +*.cache +*.docstates +_ReSharper.* +nuget.exe +*net45.csproj +*net451.csproj +*k10.csproj +*.psess +*.vsp +*.pidb +*.userprefs +*DS_Store +*.ncrunchsolution +*.*sdf +*.ipch +.vs/ +npm-debug.log +/.build/ +.vscode/ +global.json diff --git a/src/JavaScriptServices/Directory.Build.props b/src/JavaScriptServices/Directory.Build.props new file mode 100644 index 0000000000..cec8e3da65 --- /dev/null +++ b/src/JavaScriptServices/Directory.Build.props @@ -0,0 +1,15 @@ + + + + + + + Microsoft ASP.NET Core + https://github.com/aspnet/javascriptservices + git + $(MSBuildThisFileDirectory) + $(MSBuildThisFileDirectory)build\Key.snk + true + true + + diff --git a/src/JavaScriptServices/Directory.Build.targets b/src/JavaScriptServices/Directory.Build.targets new file mode 100644 index 0000000000..78626b773e --- /dev/null +++ b/src/JavaScriptServices/Directory.Build.targets @@ -0,0 +1,10 @@ + + + $(MicrosoftNETCoreApp20PackageVersion) + $(MicrosoftNETCoreApp21PackageVersion) + $(MicrosoftNETCoreApp22PackageVersion) + $(NETStandardLibrary20PackageVersion) + + 99.9 + + diff --git a/src/JavaScriptServices/JavaScriptServices.sln b/src/JavaScriptServices/JavaScriptServices.sln new file mode 100644 index 0000000000..f01aa1a956 --- /dev/null +++ b/src/JavaScriptServices/JavaScriptServices.sln @@ -0,0 +1,93 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.16 +MinimumVisualStudioVersion = 15.0.26730.03 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{27304DDE-AFB2-4F8B-B765-E3E2F11E886C}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.NodeServices", "src\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj", "{66B77203-1469-41DF-92F2-2BE6900BD36F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.NodeServices.Sockets", "src\Microsoft.AspNetCore.NodeServices.Sockets\Microsoft.AspNetCore.NodeServices.Sockets.csproj", "{F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices", "src\Microsoft.AspNetCore.SpaServices\Microsoft.AspNetCore.SpaServices.csproj", "{66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{99EAF1FE-22C8-4526-BE78-74B24125D37F}" + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + global.json = global.json + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{23836492-E7F4-4376-85BF-A635C304AC46}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{E6A161EA-646C-4033-9090-95BE809AB8D9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LatencyTest", "samples\misc\LatencyTest\LatencyTest.csproj", "{1931B19A-EC42-4D56-B2D0-FB06D17244DA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Webpack", "samples\misc\Webpack\Webpack.csproj", "{DE479DC3-1461-4EAD-A188-4AF7AA4AE344}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodeServicesExamples", "samples\misc\NodeServicesExamples\NodeServicesExamples.csproj", "{93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{645F7363-1240-4FB6-9422-B32A327C979F}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices.Extensions", "src\Microsoft.AspNetCore.SpaServices.Extensions\Microsoft.AspNetCore.SpaServices.Extensions.csproj", "{D40BD1C4-6A6F-4213-8535-1057F3EB3400}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66B77203-1469-41DF-92F2-2BE6900BD36F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66B77203-1469-41DF-92F2-2BE6900BD36F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66B77203-1469-41DF-92F2-2BE6900BD36F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66B77203-1469-41DF-92F2-2BE6900BD36F}.Release|Any CPU.Build.0 = Release|Any CPU + {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Release|Any CPU.Build.0 = Release|Any CPU + {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Release|Any CPU.Build.0 = Release|Any CPU + {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Release|Any CPU.Build.0 = Release|Any CPU + {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Release|Any CPU.Build.0 = Release|Any CPU + {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Release|Any CPU.Build.0 = Release|Any CPU + {D40BD1C4-6A6F-4213-8535-1057F3EB3400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D40BD1C4-6A6F-4213-8535-1057F3EB3400}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D40BD1C4-6A6F-4213-8535-1057F3EB3400}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D40BD1C4-6A6F-4213-8535-1057F3EB3400}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {66B77203-1469-41DF-92F2-2BE6900BD36F} = {27304DDE-AFB2-4F8B-B765-E3E2F11E886C} + {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3} = {27304DDE-AFB2-4F8B-B765-E3E2F11E886C} + {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC} = {27304DDE-AFB2-4F8B-B765-E3E2F11E886C} + {E6A161EA-646C-4033-9090-95BE809AB8D9} = {23836492-E7F4-4376-85BF-A635C304AC46} + {1931B19A-EC42-4D56-B2D0-FB06D17244DA} = {E6A161EA-646C-4033-9090-95BE809AB8D9} + {DE479DC3-1461-4EAD-A188-4AF7AA4AE344} = {E6A161EA-646C-4033-9090-95BE809AB8D9} + {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE} = {E6A161EA-646C-4033-9090-95BE809AB8D9} + {D40BD1C4-6A6F-4213-8535-1057F3EB3400} = {27304DDE-AFB2-4F8B-B765-E3E2F11E886C} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DDF59B0D-2DEC-45D6-8667-DCB767487101} + EndGlobalSection +EndGlobal diff --git a/src/JavaScriptServices/NuGetPackageVerifier.json b/src/JavaScriptServices/NuGetPackageVerifier.json new file mode 100644 index 0000000000..22ef3c09c0 --- /dev/null +++ b/src/JavaScriptServices/NuGetPackageVerifier.json @@ -0,0 +1,7 @@ +{ + "Default": { + "rules": [ + "DefaultCompositeRule" + ] + } +} diff --git a/src/JavaScriptServices/README.md b/src/JavaScriptServices/README.md new file mode 100644 index 0000000000..5ba59473c2 --- /dev/null +++ b/src/JavaScriptServices/README.md @@ -0,0 +1,96 @@ +# JavaScriptServices + +AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/gprilrckx116vc9m/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/javascriptservices/branch/dev) + +This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo. + +## What is this? + +`JavaScriptServices` is a set of client-side technologies for ASP.NET Core. It provides infrastructure that you'll find useful if you: + +- Use Angular / React / Vue / Aurelia / Knockout / etc. +- Build your client-side resources using Webpack. +- Execute JavaScript on the server at runtime. + +Read [Building Single Page Applications on ASP.NET Core with JavaScriptServices](https://blogs.msdn.microsoft.com/webdev/2017/02/14/building-single-page-applications-on-asp-net-core-with-javascriptservices/) for more details. + +This repo contains: + + * A set of NuGet/NPM packages that implement functionality for: + * Invoking arbitrary NPM packages at runtime from .NET code ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.NodeServices#simple-usage-example)) + * Server-side prerendering of SPA components ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#server-side-prerendering)) + * Webpack dev middleware ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#webpack-dev-middleware)) + * Hot module replacement (HMR) ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#webpack-hot-module-replacement)) + * Server-side and client-side routing integration ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#routing-helper-mapspafallbackroute)) + * Server-side and client-side validation integration + * "Lazy loading" for Knockout apps + * Samples and docs + +It's cross-platform (Windows, Linux, or macOS) and works with .NET Core 2.0 or later. + +## Creating new applications + +Prerequisites: + +* [.NET Core 2.0](https://www.microsoft.com/net/core) (or later) SDK +* [Node.js](https://nodejs.org/) version 6 (or later) + +With these prerequisites, you can immediately create new ASP.NET Core applications that use Angular, React, or React+Redux without having to install anything extra. + +### Option 1: Creating Angular/React/Redux applications from the command line (cross-platform) + +In an empty directory, run (for example) `dotnet new angular`. Other supported SPA frameworks include React and React+Redux. You can see the list of available SPA templates by running `dotnet new spa`. + +Once the generator has run and restored all the dependencies, you can start up your new ASP.NET Core SPA: + + npm install + dotnet run + +### Option 2: Creating Angular/React/Redux applications using Visual Studio 2017 Update 3 or later (Windows only) + +Using the `File`->`New Project` dialog, select *ASP.NET Core Web Application*. You will then be offered the option to create an application with Angular, React, or React+Redux. When the application is created, you can build and run it in the normal way. + +### More info and other SPA frameworks + +For a more detailed (albeit somewhat outdated) walkthrough, see [getting started with the `aspnetcore-spa` generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/). + +If you want to build an ASP.NET Core application with Aurelia, Knockout, or Vue, you can use the `Microsoft.AspNetCore.SpaTemplates` package. On the command line, run `dotnet new --install Microsoft.AspNetCore.SpaTemplates`. Then you will be able to run `dotnet new aurelia` (or `dotnet new vue`, etc.) to create your new application. + +## Adding to existing applications + +If you have an existing ASP.NET Core application, or if you just want to use the underlying JavaScriptServices packages directly, you can install these packages using NuGet and NPM: + + * `Microsoft.AspNetCore.NodeServices` + * This provides a fast and robust way for .NET code to run JavaScript on the server inside a Node.js environment. You can use this to consume arbitrary functionality from NPM packages at runtime in your ASP.NET Core app. + * Most applications developers don't need to use this directly, but you can do so if you want to implement your own functionality that involves calling Node.js code from .NET at runtime. + * Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.NodeServices#microsoftaspnetcorenodeservices). + * `Microsoft.AspNetCore.SpaServices` + * This provides infrastructure that's generally useful when building Single Page Applications (SPAs) with technologies such as Angular or React (for example, server-side prerendering and webpack middleware). Internally, it uses the `NodeServices` package to implement its features. + * Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#microsoftaspnetcorespaservices) + +There were previously other packages called `Microsoft.AspNetCore.AngularServices` and `Microsoft.AspNetCore.ReactServices` but these are not currently needed - all applicable functionality is in `Microsoft.AspNetCore.SpaServices`, because it's sufficiently general. + +If you want to build a helper library for some other SPA framework, you can do so by taking a dependency on `Microsoft.AspNetCore.SpaServices` and wrapping its functionality in whatever way is most useful for your SPA framework. + +## Samples + +The [`samples` directory](https://github.com/aspnet/JavaScriptServices/tree/dev/samples) contains examples of: + +- Using the JavaScript services family of packages with Angular and React. +- A standalone `NodeServices` usage for runtime code transpilation and image processing. + +**To run the samples:** + + * Clone this repo + * At the repo's root directory (the one containing `src`, `samples`, etc.), run `dotnet restore` + * Change directory to the sample you want to run (for example, `cd samples/angular/MusicStore`) + * Restore Node dependencies by running `npm install` + * If you're trying to run the Angular "Music Store" sample, then also run `gulp` (which you need to have installed globally). None of the other samples require this. + * Run the application (`dotnet run`) + * Browse to [http://localhost:5000](http://localhost:5000) + +## Contributing + +If you're interested in contributing to the various packages, samples, and project templates in this repo, that's great! + +Before working on a pull request, especially if it's more than a trivial fix (for example, for a typo), it's usually a good idea first to file an issue describing what you're proposing to do and how it will work. Then you can find out if it's likely that such a pull request will be accepted, and how it fits into wider ongoing plans. diff --git a/src/JavaScriptServices/build/Key.snk b/src/JavaScriptServices/build/Key.snk new file mode 100644 index 0000000000..e10e4889c1 Binary files /dev/null and b/src/JavaScriptServices/build/Key.snk differ diff --git a/src/JavaScriptServices/build/dependencies.props b/src/JavaScriptServices/build/dependencies.props new file mode 100644 index 0000000000..fec3d049b2 --- /dev/null +++ b/src/JavaScriptServices/build/dependencies.props @@ -0,0 +1,31 @@ + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + 2.2.0-preview2-20181004.6 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.0.9 + 2.1.3 + 2.2.0-preview3-27001-02 + 2.2.0-preview3-35425 + 2.0.3 + 11.0.2 + 4.9.0 + + + + diff --git a/src/JavaScriptServices/build/repo.props b/src/JavaScriptServices/build/repo.props new file mode 100644 index 0000000000..f1fe24dd27 --- /dev/null +++ b/src/JavaScriptServices/build/repo.props @@ -0,0 +1,16 @@ + + + + + + Internal.AspNetCore.Universe.Lineup + 2.2.0-* + https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json + + + + + + + + diff --git a/src/JavaScriptServices/build/sources.props b/src/JavaScriptServices/build/sources.props new file mode 100644 index 0000000000..9215df9751 --- /dev/null +++ b/src/JavaScriptServices/build/sources.props @@ -0,0 +1,17 @@ + + + + + $(DotNetRestoreSources) + + $(RestoreSources); + https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; + https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; + https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; + + + $(RestoreSources); + https://api.nuget.org/v3/index.json; + + + diff --git a/src/JavaScriptServices/samples/misc/LatencyTest/LatencyTest.csproj b/src/JavaScriptServices/samples/misc/LatencyTest/LatencyTest.csproj new file mode 100644 index 0000000000..c29edd1972 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/LatencyTest/LatencyTest.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp2.2;net461 + false + exe + + + + + + + + + + + + diff --git a/src/JavaScriptServices/samples/misc/LatencyTest/Program.cs b/src/JavaScriptServices/samples/misc/LatencyTest/Program.cs new file mode 100644 index 0000000000..bafe4b9fbc --- /dev/null +++ b/src/JavaScriptServices/samples/misc/LatencyTest/Program.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.NodeServices.Sockets; +using Microsoft.Extensions.DependencyInjection; + +namespace ConsoleApplication +{ + // This project is a micro-benchmark for .NET->Node RPC via NodeServices. It doesn't reflect + // real-world usage patterns (you're not likely to make hundreds of sequential calls like this), + // but is a starting point for comparing the overhead of different hosting models and transports. + public class Program + { + public static void Main(string[] args) { + // Set up the DI system + var services = new ServiceCollection(); + services.AddNodeServices(options => { + // To compare with Socket hosting, uncomment the following line + // Since .NET Core 1.1, the HTTP hosting model has become basically as fast as the Socket hosting model + //options.UseSocketHosting(); + + options.WatchFileExtensions = new string[] {}; // Don't watch anything + }); + var serviceProvider = services.BuildServiceProvider(); + + // Now instantiate an INodeServices and use it + using (var nodeServices = serviceProvider.GetRequiredService()) { + MeasureLatency(nodeServices).Wait(); + } + } + + private static async Task MeasureLatency(INodeServices nodeServices) { + // Ensure the connection is open, so we can measure per-request timings below + var response = await nodeServices.InvokeAsync("latencyTest", "C#"); + Console.WriteLine(response); + + // Now perform a series of requests, capturing the time taken + const int requestCount = 100; + var watch = Stopwatch.StartNew(); + for (var i = 0; i < requestCount; i++) { + await nodeServices.InvokeAsync("latencyTest", "C#"); + } + + // Display results + var elapsedSeconds = (float)watch.ElapsedTicks / Stopwatch.Frequency; + Console.WriteLine("\nTotal time: {0:F2} milliseconds", 1000 * elapsedSeconds); + Console.WriteLine("\nTime per invocation: {0:F2} milliseconds", 1000 * elapsedSeconds / requestCount); + } + } +} diff --git a/src/JavaScriptServices/samples/misc/LatencyTest/latencyTest.js b/src/JavaScriptServices/samples/misc/LatencyTest/latencyTest.js new file mode 100644 index 0000000000..9feb344a32 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/LatencyTest/latencyTest.js @@ -0,0 +1,4 @@ +module.exports = function(callback, incomingParam1) { + var result = 'Hello, ' + incomingParam1 + '!'; + callback(/* error */ null, result); +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/.gitignore b/src/JavaScriptServices/samples/misc/NodeServicesExamples/.gitignore new file mode 100644 index 0000000000..6330d42a2b --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/Properties/launchSettings.json diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Controllers/HomeController.cs b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Controllers/HomeController.cs new file mode 100644 index 0000000000..d9f32410eb --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Controllers/HomeController.cs @@ -0,0 +1,57 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.SpaServices.Prerendering; + +namespace NodeServicesExamples.Controllers +{ + public class HomeController : Controller + { + public IActionResult Index(int pageIndex) + { + return View(); + } + + public IActionResult ES2015Transpilation() + { + return View(); + } + + public async Task Chart([FromServices] INodeServices nodeServices) + { + var options = new { width = 400, height = 200, showArea = true, showPoint = true, fullWidth = true }; + var data = new + { + labels = new[] { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, + series = new[] { + new[] { 1, 5, 2, 5, 4, 3 }, + new[] { 2, 3, 4, 8, 1, 2 }, + new[] { 5, 4, 3, 2, 1, 0 } + } + }; + + ViewData["ChartMarkup"] = await nodeServices.InvokeAsync("./Node/renderChart", "line", options, data); + + return View(); + } + + public async Task Prerendering([FromServices] ISpaPrerenderer prerenderer) + { + var result = await prerenderer.RenderToString("./Node/prerenderPage"); + + if (!string.IsNullOrEmpty(result.RedirectUrl)) + { + return Redirect(result.RedirectUrl); + } + + ViewData["PrerenderedHtml"] = result.Html; + ViewData["PrerenderedGlobals"] = result.CreateGlobalsAssignmentScript(); + return View(); + } + + public IActionResult Error() + { + return View("~/Views/Shared/Error.cshtml"); + } + } +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/prerenderPage.js b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/prerenderPage.js new file mode 100644 index 0000000000..7912a6f3cb --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/prerenderPage.js @@ -0,0 +1,14 @@ +var createServerRenderer = require('aspnet-prerendering').createServerRenderer; + +module.exports = createServerRenderer(function(params) { + return new Promise(function (resolve, reject) { + var message = 'The HTML was returned by the prerendering boot function. ' + + 'The boot function received the following params:' + + '
' + JSON.stringify(params, null, 4) + '
'; + + resolve({ + html: '

Hello, world!

' + message, + globals: { sampleData: { nodeVersion: process.version } } + }); + }); +}); diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/renderChart.js b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/renderChart.js new file mode 100644 index 0000000000..370df76654 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/renderChart.js @@ -0,0 +1,8 @@ +var generate = require('node-chartist'); + +module.exports = function (callback, type, options, data) { + generate(type, options, data).then( + result => callback(null, result), // Success case + error => callback(error) // Error case + ); +}; diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/transpilation.js b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/transpilation.js new file mode 100644 index 0000000000..86f605ac94 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Node/transpilation.js @@ -0,0 +1,12 @@ +var fs = require('fs'); +var babelCore = require('babel-core'); + +module.exports = function(cb, physicalPath, requestPath) { + var originalContents = fs.readFileSync(physicalPath); + var result = babelCore.transform(originalContents, { + presets: ['es2015'], + sourceMaps: 'inline', + sourceFileName: '/sourcemapped' + requestPath + }); + cb(null, result.code); +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj b/src/JavaScriptServices/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj new file mode 100644 index 0000000000..5a5a76247f --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp2.2;net461 + true + false + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Startup.cs b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Startup.cs new file mode 100644 index 0000000000..26f2ae86c4 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Startup.cs @@ -0,0 +1,70 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System.IO; + +namespace NodeServicesExamples +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + + // Enable Node Services + services.AddNodeServices(); + services.AddSpaPrerenderer(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IHostingEnvironment env, INodeServices nodeServices) + { + app.UseDeveloperExceptionPage(); + + // Dynamically transpile any .js files under the '/js/' directory + app.Use(next => async context => { + var requestPath = context.Request.Path.Value; + if (requestPath.StartsWith("/js/") && requestPath.EndsWith(".js")) { + var fileInfo = env.WebRootFileProvider.GetFileInfo(requestPath); + if (fileInfo.Exists) { + var transpiled = await nodeServices.InvokeAsync("./Node/transpilation.js", fileInfo.PhysicalPath, requestPath); + await context.Response.WriteAsync(transpiled); + return; + } + } + + // Not a JS file, or doesn't exist - let some other middleware handle it + await next.Invoke(context); + }); + + app.UseStaticFiles(); + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .ConfigureLogging(factory => + { + factory.AddConsole(); + factory.AddDebug(); + }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseKestrel() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Chart.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Chart.cshtml new file mode 100644 index 0000000000..2775261e26 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Chart.cshtml @@ -0,0 +1,12 @@ +

Server-rendered chart

+ +

+ This sample demonstrates how arbitrary NPM modules can be invoked from .NET code. +

+

+ In this case, we use node-chartist to render the following chart on the server. The output is + identical to what you'd get if you used chartist.js + on the client, except that in this example, we're not executing any client-side code at all. +

+ +@Html.Raw(ViewData["ChartMarkup"]) diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/ES2015Transpilation.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/ES2015Transpilation.cshtml new file mode 100644 index 0000000000..bfb35ec5c6 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/ES2015Transpilation.cshtml @@ -0,0 +1,16 @@ +

ES2015 Transpilation

+ +

+ This sample demonstrates a way of intercepting requests for .js files and dynamically transpiling them + from ES2015 code to browser-compatible ES5 code using the Babel library. +

+ +

+ To see that it's working, open your browser's 'Debug' console and look for the log message. This is + produced by the file /js/main.js, which is transpiled from ES2015 dynamically + when requested. +

+ +@section scripts { + +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Index.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Index.cshtml new file mode 100644 index 0000000000..bbdc56316d --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Index.cshtml @@ -0,0 +1,13 @@ +

NodeServices examples

+ +

+ These examples demonstrate the direct use of the NodeServices package, independently of the usual SPA scenarios. + In general, NodeServices offers an efficient way to use Node-provided functionality (e.g., NPM modules) from inside + a .NET application. +

+ + diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Prerendering.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Prerendering.cshtml new file mode 100644 index 0000000000..bd5fff9df2 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Home/Prerendering.cshtml @@ -0,0 +1,21 @@ +

Server-side prerendering

+ +

+ This sample demonstrates how you can invoke a JavaScript module that contains + prerendering logic for a Single-Page Application framework. +

+

+ Your prerendering boot function will receive parameters that describe the page + being rendered and any data supplied by the .NET code. The return value should be + a promise that resolves with data to be injected into the page, such as the + rendered HTML and any global data that should be made available to client-side code. +

+ +@Html.Raw(ViewData["PrerenderedHtml"]) + + + + diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Shared/Error.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Shared/Error.cshtml new file mode 100644 index 0000000000..473b35d6ca --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Shared/Error.cshtml @@ -0,0 +1,6 @@ +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Shared/_Layout.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..93314312ad --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/Shared/_Layout.cshtml @@ -0,0 +1,12 @@ + + + + + NodeServices Examples + + + + @RenderBody() + @RenderSection("scripts", required: false) + + diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/_ViewImports.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/_ViewImports.cshtml new file mode 100644 index 0000000000..340f37c60e --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@using NodeServicesExamples +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/_ViewStart.cshtml b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..820a2f6e02 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/appsettings.json b/src/JavaScriptServices/samples/misc/NodeServicesExamples/appsettings.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/appsettings.json @@ -0,0 +1 @@ +{} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/jsconfig.json b/src/JavaScriptServices/samples/misc/NodeServicesExamples/jsconfig.json new file mode 100644 index 0000000000..875bb90cd6 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/jsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs" + } +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/package.json b/src/JavaScriptServices/samples/misc/NodeServicesExamples/package.json new file mode 100644 index 0000000000..9787758bc4 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/package.json @@ -0,0 +1,10 @@ +{ + "name": "nodeservicesexamples", + "version": "0.0.0", + "dependencies": { + "aspnet-prerendering": "^2.0.6", + "babel-core": "^6.7.4", + "babel-preset-es2015": "^6.6.0", + "node-chartist": "^1.0.2" + } +} diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/css/chartist.min.css b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/css/chartist.min.css new file mode 100644 index 0000000000..9f9b908e71 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/css/chartist.min.css @@ -0,0 +1 @@ +.ct-double-octave:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-grid-background,.ct-line{fill:none}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0} \ No newline at end of file diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/favicon.ico b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/favicon.ico new file mode 100644 index 0000000000..a3a799985c Binary files /dev/null and b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/favicon.ico differ diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/js/main.js b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/js/main.js new file mode 100644 index 0000000000..97780c1ce9 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/js/main.js @@ -0,0 +1,7 @@ +class Greeting { + getMessage() { + return 'Hello from the ES2015 class'; + } +} + +console.log(new Greeting().getMessage()); diff --git a/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/web.config b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/web.config new file mode 100644 index 0000000000..bb50c71648 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/NodeServicesExamples/wwwroot/web.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/JavaScriptServices/samples/misc/Webpack/.gitignore b/src/JavaScriptServices/samples/misc/Webpack/.gitignore new file mode 100644 index 0000000000..1363491964 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/.gitignore @@ -0,0 +1,233 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +yarn.lock +wwwroot/dist + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/src/JavaScriptServices/samples/misc/Webpack/ActionResults/PrerenderResult.cs b/src/JavaScriptServices/samples/misc/Webpack/ActionResults/PrerenderResult.cs new file mode 100644 index 0000000000..4f2c8f50c9 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/ActionResults/PrerenderResult.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.SpaServices.Prerendering; +using Microsoft.Extensions.DependencyInjection; + +namespace Webpack.ActionResults +{ + // This is an example of how you could invoke the prerendering API from an ActionResult, so as to + // prerender a SPA component as the entire response page (instead of injecting the SPA component + // into a Razor view's output) + public class PrerenderResult : ActionResult + { + private JavaScriptModuleExport _moduleExport; + private object _dataToSupply; + + public PrerenderResult(JavaScriptModuleExport moduleExport, object dataToSupply = null) + { + _moduleExport = moduleExport; + _dataToSupply = dataToSupply; + } + + public override async Task ExecuteResultAsync(ActionContext context) + { + var nodeServices = context.HttpContext.RequestServices.GetRequiredService(); + var hostEnv = context.HttpContext.RequestServices.GetRequiredService(); + var applicationLifetime = context.HttpContext.RequestServices.GetRequiredService(); + var applicationBasePath = hostEnv.ContentRootPath; + var request = context.HttpContext.Request; + var response = context.HttpContext.Response; + + var prerenderedHtml = await Prerenderer.RenderToString( + applicationBasePath, + nodeServices, + applicationLifetime.ApplicationStopping, + _moduleExport, + request.GetEncodedUrl(), + request.Path + request.QueryString.Value, + _dataToSupply, + /* timeoutMilliseconds */ 30000, + /* requestPathBase */ "/" + ); + + response.ContentType = "text/html"; + await response.WriteAsync(prerenderedHtml.Html); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs b/src/JavaScriptServices/samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs new file mode 100644 index 0000000000..926e114999 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SpaServices.Prerendering; + +namespace Webpack.ActionResults +{ + public static class PrerenderResultExtensions + { + public static PrerenderResult Prerender(this ControllerBase controller, JavaScriptModuleExport exportToPrerender, object dataToSupply = null) + { + return new PrerenderResult(exportToPrerender, dataToSupply); + } + } +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/Clientside/App.ts b/src/JavaScriptServices/samples/misc/Webpack/Clientside/App.ts new file mode 100644 index 0000000000..07a4e7733f --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Clientside/App.ts @@ -0,0 +1,4 @@ +import { HelloWorld } from './HelloWorld'; +import './styles/main.less'; + +new HelloWorld().doIt(); diff --git a/src/JavaScriptServices/samples/misc/Webpack/Clientside/HelloWorld.ts b/src/JavaScriptServices/samples/misc/Webpack/Clientside/HelloWorld.ts new file mode 100644 index 0000000000..512dcf94c4 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Clientside/HelloWorld.ts @@ -0,0 +1,5 @@ +export class HelloWorld { + public doIt() { + console.log('Hello from MyApp'); + } +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/Clientside/styles/main.less b/src/JavaScriptServices/samples/misc/Webpack/Clientside/styles/main.less new file mode 100644 index 0000000000..ea61843fc2 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Clientside/styles/main.less @@ -0,0 +1,5 @@ +@headerColor: red; + +h1 { + color: @headerColor; +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/Controllers/HomeController.cs b/src/JavaScriptServices/samples/misc/Webpack/Controllers/HomeController.cs new file mode 100644 index 0000000000..136e94e0a3 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Controllers/HomeController.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Webpack.Controllers +{ + public class HomeController : Controller + { + public IActionResult Index() + { + return View(); + } + } +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/Properties/launchSettings.json b/src/JavaScriptServices/samples/misc/Webpack/Properties/launchSettings.json new file mode 100644 index 0000000000..f2904dc8ae --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51463/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "dotnet cli": { + "commandName": "Project", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/samples/misc/Webpack/Startup.cs b/src/JavaScriptServices/samples/misc/Webpack/Startup.cs new file mode 100644 index 0000000000..a471781e09 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Startup.cs @@ -0,0 +1,60 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SpaServices.Webpack; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System.IO; +using Microsoft.AspNetCore.NodeServices; + +namespace Webpack +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + services.AddNodeServices(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IHostingEnvironment env) + { + app.UseDeveloperExceptionPage(); + + // For real apps, you should only use Webpack Dev Middleware at development time. For production, + // you'll get better performance and reliability if you precompile the webpack output and simply + // serve the resulting static files. For examples of setting up this automatic switch between + // development-style and production-style webpack usage, see the 'templates' dir in this repo. + app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { + HotModuleReplacement = true + }); + + app.UseStaticFiles(); + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .ConfigureLogging(factory => + { + factory.AddConsole(); + factory.AddDebug(); + }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseKestrel() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/Views/Home/Index.cshtml b/src/JavaScriptServices/samples/misc/Webpack/Views/Home/Index.cshtml new file mode 100644 index 0000000000..7828ec195f --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Views/Home/Index.cshtml @@ -0,0 +1,10 @@ +@{ + ViewData["Title"] = "Home Page"; +} + +

Hello

+Hi there. Enter some text: + +@section scripts { + +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/Views/Shared/Error.cshtml b/src/JavaScriptServices/samples/misc/Webpack/Views/Shared/Error.cshtml new file mode 100644 index 0000000000..473b35d6ca --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Views/Shared/Error.cshtml @@ -0,0 +1,6 @@ +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

diff --git a/src/JavaScriptServices/samples/misc/Webpack/Views/Shared/_Layout.cshtml b/src/JavaScriptServices/samples/misc/Webpack/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..929671831e --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Views/Shared/_Layout.cshtml @@ -0,0 +1,14 @@ + + + + + @ViewData["Title"] + + + + + + @RenderBody() + @RenderSection("scripts", required: false) + + diff --git a/src/JavaScriptServices/samples/misc/Webpack/Views/_ViewImports.cshtml b/src/JavaScriptServices/samples/misc/Webpack/Views/_ViewImports.cshtml new file mode 100644 index 0000000000..de3b5a18ab --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@using Webpack +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" diff --git a/src/JavaScriptServices/samples/misc/Webpack/Views/_ViewStart.cshtml b/src/JavaScriptServices/samples/misc/Webpack/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..820a2f6e02 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/Webpack.csproj b/src/JavaScriptServices/samples/misc/Webpack/Webpack.csproj new file mode 100644 index 0000000000..5a5a76247f --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/Webpack.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp2.2;net461 + true + false + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/JavaScriptServices/samples/misc/Webpack/appsettings.json b/src/JavaScriptServices/samples/misc/Webpack/appsettings.json new file mode 100644 index 0000000000..e5472e562b --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Verbose", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/package.json b/src/JavaScriptServices/samples/misc/Webpack/package.json new file mode 100644 index 0000000000..3216132bb9 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/package.json @@ -0,0 +1,20 @@ +{ + "name": "Webpack", + "version": "0.0.0", + "devDependencies": { + "css-loader": "^0.23.1", + "extendify": "^1.0.0", + "extract-text-webpack-plugin": "^1.0.1", + "less": "^2.6.0", + "less-loader": "^2.2.2", + "style-loader": "^0.13.0", + "webpack-hot-middleware": "^2.7.1" + }, + "dependencies": { + "aspnet-prerendering": "^1.0.4", + "aspnet-webpack": "^1.0.3", + "ts-loader": "^0.8.1", + "typescript": "^2.0.0", + "webpack": "^1.13.3" + } +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/tsconfig.json b/src/JavaScriptServices/samples/misc/Webpack/tsconfig.json new file mode 100644 index 0000000000..453b7a3308 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "commonjs", + "target": "es5", + "jsx": "preserve", + "sourceMap": true, + "lib": ["es6", "dom"] + }, + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/samples/misc/Webpack/webpack.config.dev.js b/src/JavaScriptServices/samples/misc/Webpack/webpack.config.dev.js new file mode 100644 index 0000000000..fc339d2e6e --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/webpack.config.dev.js @@ -0,0 +1,8 @@ +module.exports = { + devtool: 'inline-source-map', + module: { + loaders: [ + { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' } + ] + } +}; diff --git a/src/JavaScriptServices/samples/misc/Webpack/webpack.config.js b/src/JavaScriptServices/samples/misc/Webpack/webpack.config.js new file mode 100644 index 0000000000..011ed5f2e5 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/webpack.config.js @@ -0,0 +1,25 @@ +var path = require('path'); +var merge = require('extendify')({ isDeep: true, arrays: 'concat' }); +var devConfig = require('./webpack.config.dev'); +var prodConfig = require('./webpack.config.prod'); +var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Development'; + +module.exports = merge({ + resolve: { + extensions: [ '', '.js', '.jsx', '.ts', '.tsx' ] + }, + module: { + loaders: [ + { test: /\.ts(x?)$/, exclude: /node_modules/, loader: 'ts-loader?silent' } + ], + }, + entry: { + main: ['./Clientside/App.ts'] + }, + output: { + path: path.join(__dirname, 'wwwroot', 'dist'), + filename: '[name].js', + publicPath: '/dist/' + }, + plugins: [] +}, isDevelopment ? devConfig : prodConfig); diff --git a/src/JavaScriptServices/samples/misc/Webpack/webpack.config.prod.js b/src/JavaScriptServices/samples/misc/Webpack/webpack.config.prod.js new file mode 100644 index 0000000000..56203448a9 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/webpack.config.prod.js @@ -0,0 +1,15 @@ +var webpack = require('webpack'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var extractLESS = new ExtractTextPlugin('my-styles.css'); + +module.exports = { + module: { + loaders: [ + { test: /\.less$/, loader: extractLESS.extract(['css-loader', 'less-loader']) }, + ] + }, + plugins: [ + extractLESS, + new webpack.optimize.UglifyJsPlugin({ minimize: true, compressor: { warnings: false } }) + ] +}; diff --git a/src/JavaScriptServices/samples/misc/Webpack/wwwroot/favicon.ico b/src/JavaScriptServices/samples/misc/Webpack/wwwroot/favicon.ico new file mode 100644 index 0000000000..a3a799985c Binary files /dev/null and b/src/JavaScriptServices/samples/misc/Webpack/wwwroot/favicon.ico differ diff --git a/src/JavaScriptServices/samples/misc/Webpack/wwwroot/web.config b/src/JavaScriptServices/samples/misc/Webpack/wwwroot/web.config new file mode 100644 index 0000000000..e70a7778d6 --- /dev/null +++ b/src/JavaScriptServices/samples/misc/Webpack/wwwroot/web.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/JavaScriptServices/scripts/Regenerate-JSFiles.ps1 b/src/JavaScriptServices/scripts/Regenerate-JSFiles.ps1 new file mode 100644 index 0000000000..ef3336c8f0 --- /dev/null +++ b/src/JavaScriptServices/scripts/Regenerate-JSFiles.ps1 @@ -0,0 +1,28 @@ +[cmdletbinding(SupportsShouldProcess = $true)] +param( +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2 + +Push-Location "src" +try { + $dirs = Get-ChildItem -Directory + foreach($dir in $dirs) + { + Push-Location $dir + try{ + if(Test-Path -Path "package.json") + { + npm install + npm run build + } + } + finally{ + Pop-Location + } + } +} +finally { + Pop-Location +} diff --git a/src/JavaScriptServices/src/Directory.Build.props b/src/JavaScriptServices/src/Directory.Build.props new file mode 100644 index 0000000000..272b8171cc --- /dev/null +++ b/src/JavaScriptServices/src/Directory.Build.props @@ -0,0 +1,13 @@ + + + + + true + true + aspnetcore;aspnetcoremvc;nodeservices + + + + + + diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/.gitignore new file mode 100644 index 0000000000..98edee716e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/node_modules/ +yarn.lock diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/Content/Node/entrypoint-socket.js b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/Content/Node/entrypoint-socket.js new file mode 100644 index 0000000000..7bd947fa31 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/Content/Node/entrypoint-socket.js @@ -0,0 +1,604 @@ +(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(1); + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive, +// but simplifies things for the consumer of this module. +__webpack_require__(2); +var net = __webpack_require__(3); +var path = __webpack_require__(4); +var readline = __webpack_require__(5); +var ArgsUtil_1 = __webpack_require__(6); +var ExitWhenParentExits_1 = __webpack_require__(7); +var virtualConnectionServer = __webpack_require__(8); +// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct +// reference to Node's runtime 'require' function. +var dynamicRequire = eval('require'); +// Signal to the .NET side when we're ready to accept invocations +var server = net.createServer().on('listening', function () { + console.log('[Microsoft.AspNetCore.NodeServices:Listening]'); +}); +// Each virtual connection represents a separate invocation +virtualConnectionServer.createInterface(server).on('connection', function (connection) { + readline.createInterface(connection, null).on('line', function (line) { + try { + // Get a reference to the function to invoke + var invocation = JSON.parse(line); + var invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName)); + var invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule; + // Prepare a callback for accepting non-streamed JSON responses + var hasInvokedCallback_1 = false; + var invocationCallback = function (errorValue, successValue) { + if (hasInvokedCallback_1) { + throw new Error('Cannot supply more than one result. The callback has already been invoked,' + + ' or the result stream has already been accessed'); + } + hasInvokedCallback_1 = true; + connection.end(JSON.stringify({ + result: successValue, + errorMessage: errorValue && (errorValue.message || errorValue), + errorDetails: errorValue && (errorValue.stack || null) + })); + }; + // Also support streamed binary responses + Object.defineProperty(invocationCallback, 'stream', { + enumerable: true, + get: function () { + hasInvokedCallback_1 = true; + return connection; + } + }); + // Actually invoke it, passing through any supplied args + invokedFunction.apply(null, [invocationCallback].concat(invocation.args)); + } + catch (ex) { + connection.end(JSON.stringify({ + errorMessage: ex.message, + errorDetails: ex.stack + })); + } + }); +}); +// Begin listening now. The underlying transport varies according to the runtime platform. +// On Windows it's Named Pipes; on Linux/OSX it's Domain Sockets. +var useWindowsNamedPipes = /^win/.test(process.platform); +var parsedArgs = ArgsUtil_1.parseArgs(process.argv); +var listenAddress = (useWindowsNamedPipes ? '\\\\.\\pipe\\' : '/tmp/') + parsedArgs.listenAddress; +server.listen(listenAddress); +ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true); + + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +// When Node writes to stdout/strerr, we capture that and convert the lines into calls on the +// active .NET ILogger. But by default, stdout/stderr don't have any way of distinguishing +// linebreaks inside log messages from the linebreaks that delimit separate log messages, +// so multiline strings will end up being written to the ILogger as multiple independent +// log messages. This makes them very hard to make sense of, especially when they represent +// something like stack traces. +// +// To fix this, we intercept stdout/stderr writes, and replace internal linebreaks with a +// marker token. When .NET receives the lines, it converts the marker tokens back to regular +// linebreaks within the logged messages. +// +// Note that it's better to do the interception at the stdout/stderr level, rather than at +// the console.log/console.error (etc.) level, because this takes place after any native +// message formatting has taken place (e.g., inserting values for % placeholders). +var findInternalNewlinesRegex = /\n(?!$)/g; +var encodedNewline = '__ns_newline__'; +encodeNewlinesWrittenToStream(process.stdout); +encodeNewlinesWrittenToStream(process.stderr); +function encodeNewlinesWrittenToStream(outputStream) { + var origWriteFunction = outputStream.write; + outputStream.write = function (value) { + // Only interfere with the write if it's definitely a string + if (typeof value === 'string') { + var argsClone = Array.prototype.slice.call(arguments, 0); + argsClone[0] = encodeNewlinesInString(value); + origWriteFunction.apply(this, argsClone); + } + else { + origWriteFunction.apply(this, arguments); + } + }; +} +function encodeNewlinesInString(str) { + return str.replace(findInternalNewlinesRegex, encodedNewline); +} + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +module.exports = require("net"); + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + +module.exports = require("path"); + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + +module.exports = require("readline"); + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +function parseArgs(args) { + // Very simplistic parsing which is sufficient for the cases needed. We don't want to bring in any external + // dependencies (such as an args-parsing library) to this file. + var result = {}; + var currentKey = null; + args.forEach(function (arg) { + if (arg.indexOf('--') === 0) { + var argName = arg.substring(2); + result[argName] = undefined; + currentKey = argName; + } + else if (currentKey) { + result[currentKey] = arg; + currentKey = null; + } + }); + return result; +} +exports.parseArgs = parseArgs; + + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/* +In general, we want the Node child processes to be terminated as soon as the parent .NET processes exit, +because we have no further use for them. If the .NET process shuts down gracefully, it will run its +finalizers, one of which (in OutOfProcessNodeInstance.cs) will kill its associated Node process immediately. + +But if the .NET process is terminated forcefully (e.g., on Linux/OSX with 'kill -9'), then it won't have +any opportunity to shut down its child processes, and by default they will keep running. In this case, it's +up to the child process to detect this has happened and terminate itself. + +There are many possible approaches to detecting when a parent process has exited, most of which behave +differently between Windows and Linux/OS X: + + - On Windows, the parent process can mark its child as being a 'job' that should auto-terminate when + the parent does (http://stackoverflow.com/a/4657392). Not cross-platform. + - The child Node process can get a callback when the parent disconnects (process.on('disconnect', ...)). + But despite http://stackoverflow.com/a/16487966, no callback fires in any case I've tested (Windows / OS X). + - The child Node process can get a callback when its stdin/stdout are disconnected, as described at + http://stackoverflow.com/a/15693934. This works well on OS X, but calling stdout.resume() on Windows + causes the process to terminate prematurely. + - I don't know why, but on Windows, it's enough to invoke process.stdin.resume(). For some reason this causes + the child Node process to exit as soon as the parent one does, but I don't see this documented anywhere. + - You can poll to see if the parent process, or your stdin/stdout connection to it, is gone + - You can directly pass a parent process PID to the child, and then have the child poll to see if it's + still running (e.g., using process.kill(pid, 0), which doesn't kill it but just tests whether it exists, + as per https://nodejs.org/api/process.html#process_process_kill_pid_signal) + - Or, on each poll, you can try writing to process.stdout. If the parent has died, then this will throw. + However I don't see this documented anywhere. It would be nice if you could just poll for whether or not + process.stdout is still connected (without actually writing to it) but I haven't found any property whose + value changes until you actually try to write to it. + +Of these, the only cross-platform approach that is actually documented as a valid strategy is simply polling +to check whether the parent PID is still running. So that's what we do here. +*/ +exports.__esModule = true; +var pollIntervalMs = 1000; +function exitWhenParentExits(parentPid, ignoreSigint) { + setInterval(function () { + if (!processExists(parentPid)) { + // Can't log anything at this point, because out stdout was connected to the parent, + // but the parent is gone. + process.exit(); + } + }, pollIntervalMs); + if (ignoreSigint) { + // Pressing ctrl+c in the terminal sends a SIGINT to all processes in the foreground process tree. + // By default, the Node process would then exit before the .NET process, because ASP.NET implements + // a delayed shutdown to allow ongoing requests to complete. + // + // This is problematic, because if Node exits first, the CopyToAsync code in ConditionalProxyMiddleware + // will experience a read fault, and logs a huge load of errors. Fortunately, since the Node process is + // already set up to shut itself down if it detects the .NET process is terminated, all we have to do is + // ignore the SIGINT. The Node process will then terminate automatically after the .NET process does. + // + // A better solution would be to have WebpackDevMiddleware listen for SIGINT and gracefully close any + // ongoing EventSource connections before letting the Node process exit, independently of the .NET + // process exiting. However, doing this well in general is very nontrivial (see all the discussion at + // https://github.com/nodejs/node/issues/2642). + process.on('SIGINT', function () { + console.log('Received SIGINT. Waiting for .NET process to exit...'); + }); + } +} +exports.exitWhenParentExits = exitWhenParentExits; +function processExists(pid) { + try { + // Sending signal 0 - on all platforms - tests whether the process exists. As long as it doesn't + // throw, that means it does exist. + process.kill(pid, 0); + return true; + } + catch (ex) { + // If the reason for the error is that we don't have permission to ask about this process, + // report that as a separate problem. + if (ex.code === 'EPERM') { + throw new Error("Attempted to check whether process " + pid + " was running, but got a permissions error."); + } + return false; + } +} + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +var events_1 = __webpack_require__(9); +var VirtualConnection_1 = __webpack_require__(10); +// Keep this in sync with the equivalent constant in the .NET code. Both sides split up their transmissions into frames with this max length, +// and both will reject longer frames. +var MaxFrameBodyLength = 16 * 1024; +/** + * Accepts connections to a net.Server and adapts them to behave as multiplexed connections. That is, for each physical socket connection, + * we track a list of 'virtual connections' whose API is a Duplex stream. The remote clients may open and close as many virtual connections + * as they wish, reading and writing to them independently, without the overhead of establishing new physical connections each time. + */ +function createInterface(server) { + var emitter = new events_1.EventEmitter(); + server.on('connection', function (socket) { + // For each physical socket connection, maintain a set of virtual connections. Issue a notification whenever + // a new virtual connections is opened. + var childSockets = new VirtualConnectionsCollection(socket, function (virtualConnection) { + emitter.emit('connection', virtualConnection); + }); + }); + return emitter; +} +exports.createInterface = createInterface; +/** + * Tracks the 'virtual connections' associated with a single physical socket connection. + */ +var VirtualConnectionsCollection = /** @class */ (function () { + function VirtualConnectionsCollection(_socket, _onVirtualConnectionCallback) { + var _this = this; + this._socket = _socket; + this._onVirtualConnectionCallback = _onVirtualConnectionCallback; + this._currentFrameHeader = null; + this._virtualConnections = {}; + // If the remote end closes the physical socket, treat all the virtual connections as being closed remotely too + this._socket.on('close', function () { + Object.getOwnPropertyNames(_this._virtualConnections).forEach(function (id) { + // A 'null' frame signals that the connection was closed remotely + _this._virtualConnections[id].onReceivedData(null); + }); + }); + this._socket.on('readable', this._onIncomingDataAvailable.bind(this)); + } + /** + * This is called whenever the underlying socket signals that it may have some data available to read. It will synchronously read as many + * message frames as it can from the underlying socket, opens virtual connections as needed, and dispatches data to them. + */ + VirtualConnectionsCollection.prototype._onIncomingDataAvailable = function () { + var exhaustedAllData = false; + while (!exhaustedAllData) { + // We might already have a pending frame header from the previous time this method ran, but if not, that's the next thing we need to read + if (this._currentFrameHeader === null) { + this._currentFrameHeader = this._readNextFrameHeader(); + } + if (this._currentFrameHeader === null) { + // There's not enough data to fill a frameheader, so wait until more arrives later + // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) + exhaustedAllData = true; + } + else { + var frameBodyLength = this._currentFrameHeader.bodyLength; + var frameBodyOrNull = frameBodyLength > 0 ? this._socket.read(this._currentFrameHeader.bodyLength) : null; + if (frameBodyOrNull !== null || frameBodyLength === 0) { + // We have a complete frame header+body pair, so we can now dispatch this to a virtual connection. We set _currentFrameHeader back to null + // so that the next thing we try to read is the next frame header. + var headerCopy = this._currentFrameHeader; + this._currentFrameHeader = null; + this._onReceivedCompleteFrame(headerCopy, frameBodyOrNull); + } + else { + // There's not enough data to fill the pending frame body, so wait until more arrives later + // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) + exhaustedAllData = true; + } + } + } + }; + VirtualConnectionsCollection.prototype._onReceivedCompleteFrame = function (header, bodyIfNotEmpty) { + // An incoming zero-length frame signals that there's no more data to read. + // Signal this to the Node stream APIs by pushing a 'null' chunk to it. + var virtualConnection = this._getOrOpenVirtualConnection(header); + virtualConnection.onReceivedData(header.bodyLength > 0 ? bodyIfNotEmpty : null); + }; + VirtualConnectionsCollection.prototype._getOrOpenVirtualConnection = function (header) { + if (this._virtualConnections.hasOwnProperty(header.connectionIdString)) { + // It's an existing virtual connection + return this._virtualConnections[header.connectionIdString]; + } + else { + // It's a new one + return this._openVirtualConnection(header); + } + }; + VirtualConnectionsCollection.prototype._openVirtualConnection = function (header) { + var _this = this; + var beginWriteCallback = function (data, writeCompletedCallback) { + // Only send nonempty frames, since empty ones are a signal to close the virtual connection + if (data.length > 0) { + _this._sendFrame(header.connectionIdBinary, data, writeCompletedCallback); + } + }; + var newVirtualConnection = new VirtualConnection_1.VirtualConnection(beginWriteCallback); + newVirtualConnection.on('end', function () { + // The virtual connection was closed remotely. Clean up locally. + _this._onVirtualConnectionWasClosed(header.connectionIdString); + }); + newVirtualConnection.on('finish', function () { + // The virtual connection was closed locally. Clean up locally, and notify the remote that we're done. + _this._onVirtualConnectionWasClosed(header.connectionIdString); + _this._sendFrame(header.connectionIdBinary, Buffer.alloc(0)); + }); + this._virtualConnections[header.connectionIdString] = newVirtualConnection; + this._onVirtualConnectionCallback(newVirtualConnection); + return newVirtualConnection; + }; + /** + * Attempts to read a complete frame header, synchronously, from the underlying socket. + * If not enough data is available synchronously, returns null without consuming any data from the socket. + */ + VirtualConnectionsCollection.prototype._readNextFrameHeader = function () { + var headerBuf = this._socket.read(12); + if (headerBuf !== null) { + // We have enough data synchronously + var connectionIdBinary = headerBuf.slice(0, 8); + var connectionIdString = connectionIdBinary.toString('hex'); + var bodyLength = headerBuf.readInt32LE(8); + if (bodyLength < 0 || bodyLength > MaxFrameBodyLength) { + // Throwing here is going to bring down the whole process, so this cannot be allowed to happen in real use. + // But it won't happen in real use, because this is only used with our .NET client, which doesn't violate this rule. + throw new Error('Illegal frame body length: ' + bodyLength); + } + return { connectionIdBinary: connectionIdBinary, connectionIdString: connectionIdString, bodyLength: bodyLength }; + } + else { + // Not enough bytes are available synchronously, so none were consumed + return null; + } + }; + VirtualConnectionsCollection.prototype._sendFrame = function (connectionIdBinary, data, callback) { + // For all sends other than the last one, only invoke the callback if it failed. + // Also, only invoke the callback at most once. + var hasInvokedCallback = false; + var finalCallback = callback && (function (error) { + if (!hasInvokedCallback) { + hasInvokedCallback = true; + callback(error); + } + }); + var notFinalCallback = callback && (function (error) { + if (error) { + finalCallback(error); + } + }); + // The amount of data we're writing might exceed MaxFrameBodyLength, so split into frames as needed. + // Note that we always send at least one frame, even if it's empty (because that's the close-virtual-connection signal). + // If needed, this could be changed to send frames asynchronously, so that large sends could proceed in parallel + // (though that would involve making a clone of 'data', to avoid the risk of it being mutated during the send). + var bytesSent = 0; + do { + var nextFrameBodyLength = Math.min(MaxFrameBodyLength, data.length - bytesSent); + var isFinalChunk = (bytesSent + nextFrameBodyLength) === data.length; + this._socket.write(connectionIdBinary, notFinalCallback); + this._sendInt32LE(nextFrameBodyLength, notFinalCallback); + this._socket.write(data.slice(bytesSent, bytesSent + nextFrameBodyLength), isFinalChunk ? finalCallback : notFinalCallback); + bytesSent += nextFrameBodyLength; + } while (bytesSent < data.length); + }; + /** + * Sends a number serialized in the correct format for .NET to receive as a System.Int32 + */ + VirtualConnectionsCollection.prototype._sendInt32LE = function (value, callback) { + var buf = Buffer.alloc(4); + buf.writeInt32LE(value, 0); + this._socket.write(buf, callback); + }; + VirtualConnectionsCollection.prototype._onVirtualConnectionWasClosed = function (id) { + if (this._virtualConnections.hasOwnProperty(id)) { + delete this._virtualConnections[id]; + } + }; + return VirtualConnectionsCollection; +}()); + + +/***/ }), +/* 9 */ +/***/ (function(module, exports) { + +module.exports = require("events"); + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + } + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var stream_1 = __webpack_require__(11); +/** + * Represents a virtual connection. Multiple virtual connections may be multiplexed over a single physical socket connection. + */ +var VirtualConnection = /** @class */ (function (_super) { + __extends(VirtualConnection, _super); + function VirtualConnection(_beginWriteCallback) { + var _this = _super.call(this) || this; + _this._beginWriteCallback = _beginWriteCallback; + _this._flowing = false; + _this._receivedDataQueue = []; + return _this; + } + VirtualConnection.prototype._read = function () { + this._flowing = true; + // Keep pushing data until we run out, or the underlying framework asks us to stop. + // When we finish, the 'flowing' state is detemined by whether more data is still being requested. + while (this._flowing && this._receivedDataQueue.length > 0) { + var nextChunk = this._receivedDataQueue.shift(); + this._flowing = this.push(nextChunk); + } + }; + VirtualConnection.prototype._write = function (chunk, encodingIfString, callback) { + if (typeof chunk === 'string') { + chunk = Buffer.from(chunk, encodingIfString); + } + this._beginWriteCallback(chunk, callback); + }; + VirtualConnection.prototype.onReceivedData = function (dataOrNullToSignalEOF) { + if (this._flowing) { + this._flowing = this.push(dataOrNullToSignalEOF); + } + else { + this._receivedDataQueue.push(dataOrNullToSignalEOF); + } + }; + return VirtualConnection; +}(stream_1.Duplex)); +exports.VirtualConnection = VirtualConnection; + + +/***/ }), +/* 11 */ +/***/ (function(module, exports) { + +module.exports = require("stream"); + +/***/ }) +/******/ ]))); \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj new file mode 100644 index 0000000000..9dab54a95d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj @@ -0,0 +1,26 @@ + + + + Socket-based RPC for Microsoft.AspNetCore.NodeServices. + netstandard2.0 + + + + + + + + + + + + + + + + + + + + + diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/NamedPipeConnection.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/NamedPipeConnection.cs new file mode 100644 index 0000000000..5fcd667907 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/NamedPipeConnection.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.IO.Pipes; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections +{ + internal class NamedPipeConnection : StreamConnection + { + private bool _disposedValue = false; + private NamedPipeClientStream _namedPipeClientStream; + +#pragma warning disable 1998 // Because in the NET451 code path, there's nothing to await + public override async Task Open(string address) + { + _namedPipeClientStream = new NamedPipeClientStream( + ".", + address, + PipeDirection.InOut, + PipeOptions.Asynchronous); + + await _namedPipeClientStream.ConnectAsync().ConfigureAwait(false); + + return _namedPipeClientStream; + } +#pragma warning restore 1998 + + public override void Dispose() + { + if (!_disposedValue) + { + if (_namedPipeClientStream != null) + { + _namedPipeClientStream.Dispose(); + } + + _disposedValue = true; + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/StreamConnection.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/StreamConnection.cs new file mode 100644 index 0000000000..cbd1f9902a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/StreamConnection.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections +{ + internal abstract class StreamConnection : IDisposable + { + public abstract Task Open(string address); + public abstract void Dispose(); + + public static StreamConnection Create() + { + var useNamedPipes = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( + System.Runtime.InteropServices.OSPlatform.Windows); + if (useNamedPipes) + { + return new NamedPipeConnection(); + } + else + { + return new UnixDomainSocketConnection(); + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketConnection.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketConnection.cs new file mode 100644 index 0000000000..6e7ebaceb4 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketConnection.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections +{ + internal class UnixDomainSocketConnection : StreamConnection + { + private bool _disposedValue = false; + private NetworkStream _networkStream; + private Socket _socket; + + public override async Task Open(string address) + { + var endPoint = new UnixDomainSocketEndPoint("/tmp/" + address); + _socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Unspecified); + await _socket.ConnectAsync(endPoint).ConfigureAwait(false); + _networkStream = new NetworkStream(_socket); + return _networkStream; + } + + public override void Dispose() + { + if (!_disposedValue) + { + if (_networkStream != null) + { + _networkStream.Dispose(); + } + + if (_socket != null) + { + _socket.Dispose(); + } + + _disposedValue = true; + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketEndPoint.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketEndPoint.cs new file mode 100644 index 0000000000..b001163293 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/PhysicalConnections/UnixDomainSocketEndPoint.cs @@ -0,0 +1,86 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections +{ + // From System.IO.Pipes/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs (an internal class in System.IO.Pipes) + internal sealed class UnixDomainSocketEndPoint : EndPoint + { + private const AddressFamily EndPointAddressFamily = AddressFamily.Unix; + + private static readonly Encoding s_pathEncoding = Encoding.UTF8; + private static readonly int s_nativePathOffset = 2; // = offsetof(struct sockaddr_un, sun_path). It's the same on Linux and OSX + private static readonly int s_nativePathLength = 91; // sockaddr_un.sun_path at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html, -1 for terminator + private static readonly int s_nativeAddressSize = s_nativePathOffset + s_nativePathLength; + + private readonly string _path; + private readonly byte[] _encodedPath; + + public UnixDomainSocketEndPoint(string path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + _path = path; + _encodedPath = s_pathEncoding.GetBytes(_path); + + if (path.Length == 0 || _encodedPath.Length > s_nativePathLength) + { + throw new ArgumentOutOfRangeException(nameof(path)); + } + } + + internal UnixDomainSocketEndPoint(SocketAddress socketAddress) + { + if (socketAddress == null) + { + throw new ArgumentNullException(nameof(socketAddress)); + } + + if (socketAddress.Family != EndPointAddressFamily || + socketAddress.Size > s_nativeAddressSize) + { + throw new ArgumentOutOfRangeException(nameof(socketAddress)); + } + + if (socketAddress.Size > s_nativePathOffset) + { + _encodedPath = new byte[socketAddress.Size - s_nativePathOffset]; + for (int i = 0; i < _encodedPath.Length; i++) + { + _encodedPath[i] = socketAddress[s_nativePathOffset + i]; + } + + _path = s_pathEncoding.GetString(_encodedPath, 0, _encodedPath.Length); + } + else + { + _encodedPath = Array.Empty(); + _path = string.Empty; + } + } + + public override SocketAddress Serialize() + { + var result = new SocketAddress(AddressFamily.Unix, s_nativeAddressSize); + + for (int index = 0; index < _encodedPath.Length; index++) + { + result[s_nativePathOffset + index] = _encodedPath[index]; + } + result[s_nativePathOffset + _encodedPath.Length] = 0; // path must be null-terminated + + return result; + } + + public override EndPoint Create(SocketAddress socketAddress) => new UnixDomainSocketEndPoint(socketAddress); + + public override AddressFamily AddressFamily => EndPointAddressFamily; + + public override string ToString() => _path; + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeInstance.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeInstance.cs new file mode 100644 index 0000000000..7559ae29db --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeInstance.cs @@ -0,0 +1,240 @@ +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.NodeServices.HostingModels; +using Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections; +using Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.NodeServices.Sockets +{ + /// + /// A specialisation of the OutOfProcessNodeInstance base class that uses a lightweight binary streaming protocol + /// to perform RPC invocations. The physical transport is Named Pipes on Windows, or Domain Sockets on Linux/Mac. + /// For details on the binary streaming protocol, see + /// The advantage versus using HTTP for RPC is that this is faster (not surprisingly - there's much less overhead + /// because we don't need most of the functionality of HTTP. + /// + /// The address of the pipe/socket is selected randomly here on the .NET side and sent to the child process as a + /// command-line argument (the address space is wide enough that there's no real risk of a clash, unlike when + /// selecting TCP port numbers). + /// + /// + internal class SocketNodeInstance : OutOfProcessNodeInstance + { + private readonly static JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + TypeNameHandling = TypeNameHandling.None + }; + + private readonly static int streamBufferSize = 16 * 1024; + private readonly static UTF8Encoding utf8EncodingWithoutBom = new UTF8Encoding(false); + + private readonly SemaphoreSlim _connectionCreationSemaphore = new SemaphoreSlim(1); + private bool _connectionHasFailed; + private StreamConnection _physicalConnection; + private string _socketAddress; + private VirtualConnectionClient _virtualConnectionClient; + + public SocketNodeInstance(NodeServicesOptions options, string socketAddress) + : base( + EmbeddedResourceReader.Read( + typeof(SocketNodeInstance), + "/Content/Node/entrypoint-socket.js"), + options.ProjectPath, + options.WatchFileExtensions, + MakeNewCommandLineOptions(socketAddress), + options.ApplicationStoppingToken, + options.NodeInstanceOutputLogger, + options.EnvironmentVariables, + options.InvocationTimeoutMilliseconds, + options.LaunchWithDebugging, + options.DebuggingPort) + { + _socketAddress = socketAddress; + } + + protected override async Task InvokeExportAsync(NodeInvocationInfo invocationInfo, CancellationToken cancellationToken) + { + if (_connectionHasFailed) + { + // _connectionHasFailed implies a protocol-level error. The old instance is no longer of any use. + var allowConnectionDraining = false; + + // This special exception type forces NodeServicesImpl to restart the Node instance + throw new NodeInvocationException( + "The SocketNodeInstance socket connection failed. See logs to identify the reason.", + details: null, + nodeInstanceUnavailable: true, + allowConnectionDraining: allowConnectionDraining); + } + + if (_virtualConnectionClient == null) + { + // Although we could pass the cancellationToken into EnsureVirtualConnectionClientCreated and + // have it signal cancellations upstream, that would be a bad thing to do, because all callers + // wait for the same connection task. There's no reason why the first caller should have the + // special ability to cancel the connection process in a way that would affect subsequent + // callers. So, each caller just independently stops awaiting connection if that call is cancelled. + await ThrowOnCancellation(EnsureVirtualConnectionClientCreated(), cancellationToken); + } + + // For each invocation, we open a new virtual connection. This gives an API equivalent to opening a new + // physical connection to the child process, but without the overhead of doing so, because it's really + // just multiplexed into the existing physical connection stream. + bool shouldDisposeVirtualConnection = true; + Stream virtualConnection = null; + try + { + virtualConnection = _virtualConnectionClient.OpenVirtualConnection(); + + // Send request + WriteJsonLine(virtualConnection, invocationInfo); + + // Determine what kind of response format is expected + if (typeof(T) == typeof(Stream)) + { + // Pass through streamed binary response + // It is up to the consumer to dispose this stream, so don't do so here + shouldDisposeVirtualConnection = false; + return (T)(object)virtualConnection; + } + else + { + // Parse and return non-streamed JSON response + var response = await ReadJsonAsync>(virtualConnection, cancellationToken); + if (response.ErrorMessage != null) + { + throw new NodeInvocationException(response.ErrorMessage, response.ErrorDetails); + } + + return response.Result; + } + } + finally + { + if (shouldDisposeVirtualConnection) + { + virtualConnection.Dispose(); + } + } + } + + private async Task EnsureVirtualConnectionClientCreated() + { + // Asynchronous equivalent to a 'lock(...) { ... }' + await _connectionCreationSemaphore.WaitAsync(); + try + { + if (_virtualConnectionClient == null) + { + _physicalConnection = StreamConnection.Create(); + + var connection = await _physicalConnection.Open(_socketAddress); + _virtualConnectionClient = new VirtualConnectionClient(connection); + _virtualConnectionClient.OnError += (ex) => + { + // This callback is fired only if there's a protocol-level failure (e.g., child process disconnected + // unexpectedly). It does *not* fire when RPC calls return errors. Since there's been a protocol-level + // failure, this Node instance is no longer usable and should be discarded. + _connectionHasFailed = true; + + OutputLogger.LogError(0, ex, ex.Message); + }; + } + } + finally + { + _connectionCreationSemaphore.Release(); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_virtualConnectionClient != null) + { + _virtualConnectionClient.Dispose(); + _virtualConnectionClient = null; + } + + if (_physicalConnection != null) + { + _physicalConnection.Dispose(); + _physicalConnection = null; + } + } + + base.Dispose(disposing); + } + + private static void WriteJsonLine(Stream stream, object serializableObject) + { + using (var streamWriter = new StreamWriter(stream, utf8EncodingWithoutBom, streamBufferSize, true)) + using (var jsonWriter = new JsonTextWriter(streamWriter)) + { + jsonWriter.CloseOutput = false; + jsonWriter.AutoCompleteOnClose = false; + + var serializer = JsonSerializer.Create(jsonSerializerSettings); + serializer.Serialize(jsonWriter, serializableObject); + jsonWriter.Flush(); + + streamWriter.WriteLine(); + streamWriter.Flush(); + } + } + + private static async Task ReadJsonAsync(Stream stream, CancellationToken cancellationToken) + { + var json = Encoding.UTF8.GetString(await ReadAllBytesAsync(stream, cancellationToken)); + return JsonConvert.DeserializeObject(json, jsonSerializerSettings); + } + + private static async Task ReadAllBytesAsync(Stream input, CancellationToken cancellationToken) + { + byte[] buffer = new byte[streamBufferSize]; + + using (var ms = new MemoryStream()) + { + int read; + while ((read = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) + { + ms.Write(buffer, 0, read); + } + + return ms.ToArray(); + } + } + + private static string MakeNewCommandLineOptions(string listenAddress) + { + return $"--listenAddress {listenAddress}"; + } + + private static Task ThrowOnCancellation(Task task, CancellationToken cancellationToken) + { + return task.IsCompleted + ? task // If the task is already completed, no need to wrap it in a further layer of task + : task.ContinueWith( + _ => {}, // If the task completes, allow execution to continue + cancellationToken, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + +#pragma warning disable 649 // These properties are populated via JSON deserialization + private class RpcJsonResponse + { + public TResult Result { get; set; } + public string ErrorMessage { get; set; } + public string ErrorDetails { get; set; } + } +#pragma warning restore 649 + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeServicesOptionsExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeServicesOptionsExtensions.cs new file mode 100644 index 0000000000..4535638678 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/SocketNodeServicesOptionsExtensions.cs @@ -0,0 +1,21 @@ +using System; + +namespace Microsoft.AspNetCore.NodeServices.Sockets +{ + /// + /// Extension methods that help with populating a object. + /// + public static class NodeServicesOptionsExtensions + { + /// + /// Configures the service so that it will use out-of-process + /// Node.js instances and perform RPC calls over binary sockets (on Windows, this is + /// implemented as named pipes; on other platforms it uses domain sockets). + /// + public static void UseSocketHosting(this NodeServicesOptions options) + { + var pipeName = "pni-" + Guid.NewGuid().ToString("D"); // Arbitrary non-clashing string + options.NodeInstanceFactory = () => new SocketNodeInstance(options, pipeName); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/SocketNodeInstanceEntryPoint.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/SocketNodeInstanceEntryPoint.ts new file mode 100644 index 0000000000..dccc63b099 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/SocketNodeInstanceEntryPoint.ts @@ -0,0 +1,79 @@ +// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive, +// but simplifies things for the consumer of this module. +import '../../Microsoft.AspNetCore.NodeServices/TypeScript/Util/OverrideStdOutputs'; +import * as net from 'net'; +import * as path from 'path'; +import * as readline from 'readline'; +import { Duplex } from 'stream'; +import { parseArgs } from '../../Microsoft.AspNetCore.NodeServices/TypeScript/Util/ArgsUtil'; +import { exitWhenParentExits } from '../../Microsoft.AspNetCore.NodeServices/TypeScript/Util/ExitWhenParentExits'; +import * as virtualConnectionServer from './VirtualConnections/VirtualConnectionServer'; + +// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct +// reference to Node's runtime 'require' function. +const dynamicRequire: (name: string) => any = eval('require'); + +// Signal to the .NET side when we're ready to accept invocations +const server = net.createServer().on('listening', () => { + console.log('[Microsoft.AspNetCore.NodeServices:Listening]'); +}); + +// Each virtual connection represents a separate invocation +virtualConnectionServer.createInterface(server).on('connection', (connection: Duplex) => { + readline.createInterface(connection, null).on('line', line => { + try { + // Get a reference to the function to invoke + const invocation = JSON.parse(line) as RpcInvocation; + const invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName)); + const invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule; + + // Prepare a callback for accepting non-streamed JSON responses + let hasInvokedCallback = false; + const invocationCallback = (errorValue, successValue) => { + if (hasInvokedCallback) { + throw new Error('Cannot supply more than one result. The callback has already been invoked,' + + ' or the result stream has already been accessed'); + } + + hasInvokedCallback = true; + connection.end(JSON.stringify({ + result: successValue, + errorMessage: errorValue && (errorValue.message || errorValue), + errorDetails: errorValue && (errorValue.stack || null) + })); + }; + + // Also support streamed binary responses + Object.defineProperty(invocationCallback, 'stream', { + enumerable: true, + get: (): Duplex => { + hasInvokedCallback = true; + return connection; + } + }); + + // Actually invoke it, passing through any supplied args + invokedFunction.apply(null, [invocationCallback].concat(invocation.args)); + } catch (ex) { + connection.end(JSON.stringify({ + errorMessage: ex.message, + errorDetails: ex.stack + })); + } + }); +}); + +// Begin listening now. The underlying transport varies according to the runtime platform. +// On Windows it's Named Pipes; on Linux/OSX it's Domain Sockets. +const useWindowsNamedPipes = /^win/.test(process.platform); +const parsedArgs = parseArgs(process.argv); +const listenAddress = (useWindowsNamedPipes ? '\\\\.\\pipe\\' : '/tmp/') + parsedArgs.listenAddress; +server.listen(listenAddress); + +exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true); + +interface RpcInvocation { + moduleName: string; + exportedFunctionName: string; + args: any[]; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnection.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnection.ts new file mode 100644 index 0000000000..763944097e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnection.ts @@ -0,0 +1,43 @@ +import { Duplex } from 'stream'; + +export type EndWriteCallback = (error?: any) => void; +export type BeginWriteCallback = (data: Buffer, callback: EndWriteCallback) => void; + +/** + * Represents a virtual connection. Multiple virtual connections may be multiplexed over a single physical socket connection. + */ +export class VirtualConnection extends Duplex { + private _flowing = false; + private _receivedDataQueue: Buffer[] = []; + + constructor(private _beginWriteCallback: BeginWriteCallback) { + super(); + } + + public _read() { + this._flowing = true; + + // Keep pushing data until we run out, or the underlying framework asks us to stop. + // When we finish, the 'flowing' state is detemined by whether more data is still being requested. + while (this._flowing && this._receivedDataQueue.length > 0) { + const nextChunk = this._receivedDataQueue.shift(); + this._flowing = this.push(nextChunk); + } + } + + public _write(chunk: Buffer | string, encodingIfString: string, callback: EndWriteCallback) { + if (typeof chunk === 'string') { + chunk = Buffer.from(chunk as string, encodingIfString); + } + + this._beginWriteCallback(chunk as Buffer, callback); + } + + public onReceivedData(dataOrNullToSignalEOF: Buffer) { + if (this._flowing) { + this._flowing = this.push(dataOrNullToSignalEOF); + } else { + this._receivedDataQueue.push(dataOrNullToSignalEOF); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnectionServer.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnectionServer.ts new file mode 100644 index 0000000000..5dbf9698a8 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnectionServer.ts @@ -0,0 +1,199 @@ +import { Server, Socket } from 'net'; +import { EventEmitter } from 'events'; +import { Duplex } from 'stream'; +import { VirtualConnection, EndWriteCallback } from './VirtualConnection'; + +// Keep this in sync with the equivalent constant in the .NET code. Both sides split up their transmissions into frames with this max length, +// and both will reject longer frames. +const MaxFrameBodyLength = 16 * 1024; + +/** + * Accepts connections to a net.Server and adapts them to behave as multiplexed connections. That is, for each physical socket connection, + * we track a list of 'virtual connections' whose API is a Duplex stream. The remote clients may open and close as many virtual connections + * as they wish, reading and writing to them independently, without the overhead of establishing new physical connections each time. + */ +export function createInterface(server: Server): EventEmitter { + const emitter = new EventEmitter(); + + server.on('connection', (socket: Socket) => { + // For each physical socket connection, maintain a set of virtual connections. Issue a notification whenever + // a new virtual connections is opened. + const childSockets = new VirtualConnectionsCollection(socket, virtualConnection => { + emitter.emit('connection', virtualConnection); + }); + }); + + return emitter; +} + +/** + * Tracks the 'virtual connections' associated with a single physical socket connection. + */ +class VirtualConnectionsCollection { + private _currentFrameHeader: FrameHeader = null; + private _virtualConnections: { [id: string]: VirtualConnection } = {}; + + constructor(private _socket: Socket, private _onVirtualConnectionCallback: (virtualConnection: Duplex) => void) { + // If the remote end closes the physical socket, treat all the virtual connections as being closed remotely too + this._socket.on('close', () => { + Object.getOwnPropertyNames(this._virtualConnections).forEach(id => { + // A 'null' frame signals that the connection was closed remotely + this._virtualConnections[id].onReceivedData(null); + }); + }); + + this._socket.on('readable', this._onIncomingDataAvailable.bind(this)); + } + + /** + * This is called whenever the underlying socket signals that it may have some data available to read. It will synchronously read as many + * message frames as it can from the underlying socket, opens virtual connections as needed, and dispatches data to them. + */ + private _onIncomingDataAvailable() { + let exhaustedAllData = false; + + while (!exhaustedAllData) { + // We might already have a pending frame header from the previous time this method ran, but if not, that's the next thing we need to read + if (this._currentFrameHeader === null) { + this._currentFrameHeader = this._readNextFrameHeader(); + } + + if (this._currentFrameHeader === null) { + // There's not enough data to fill a frameheader, so wait until more arrives later + // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) + exhaustedAllData = true; + } else { + const frameBodyLength = this._currentFrameHeader.bodyLength; + const frameBodyOrNull: Buffer = frameBodyLength > 0 ? this._socket.read(this._currentFrameHeader.bodyLength) : null; + if (frameBodyOrNull !== null || frameBodyLength === 0) { + // We have a complete frame header+body pair, so we can now dispatch this to a virtual connection. We set _currentFrameHeader back to null + // so that the next thing we try to read is the next frame header. + const headerCopy = this._currentFrameHeader; + this._currentFrameHeader = null; + this._onReceivedCompleteFrame(headerCopy, frameBodyOrNull); + } else { + // There's not enough data to fill the pending frame body, so wait until more arrives later + // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) + exhaustedAllData = true; + } + } + } + } + + private _onReceivedCompleteFrame(header: FrameHeader, bodyIfNotEmpty: Buffer) { + // An incoming zero-length frame signals that there's no more data to read. + // Signal this to the Node stream APIs by pushing a 'null' chunk to it. + const virtualConnection = this._getOrOpenVirtualConnection(header); + virtualConnection.onReceivedData(header.bodyLength > 0 ? bodyIfNotEmpty : null); + } + + private _getOrOpenVirtualConnection(header: FrameHeader) { + if (this._virtualConnections.hasOwnProperty(header.connectionIdString)) { + // It's an existing virtual connection + return this._virtualConnections[header.connectionIdString]; + } else { + // It's a new one + return this._openVirtualConnection(header); + } + } + + private _openVirtualConnection(header: FrameHeader) { + const beginWriteCallback = (data, writeCompletedCallback) => { + // Only send nonempty frames, since empty ones are a signal to close the virtual connection + if (data.length > 0) { + this._sendFrame(header.connectionIdBinary, data, writeCompletedCallback); + } + }; + + const newVirtualConnection = new VirtualConnection(beginWriteCallback); + newVirtualConnection.on('end', () => { + // The virtual connection was closed remotely. Clean up locally. + this._onVirtualConnectionWasClosed(header.connectionIdString); + }); + newVirtualConnection.on('finish', () => { + // The virtual connection was closed locally. Clean up locally, and notify the remote that we're done. + this._onVirtualConnectionWasClosed(header.connectionIdString); + this._sendFrame(header.connectionIdBinary, Buffer.alloc(0)); + }); + + this._virtualConnections[header.connectionIdString] = newVirtualConnection; + this._onVirtualConnectionCallback(newVirtualConnection); + return newVirtualConnection; + } + + /** + * Attempts to read a complete frame header, synchronously, from the underlying socket. + * If not enough data is available synchronously, returns null without consuming any data from the socket. + */ + private _readNextFrameHeader(): FrameHeader { + const headerBuf: Buffer = this._socket.read(12); + if (headerBuf !== null) { + // We have enough data synchronously + const connectionIdBinary = headerBuf.slice(0, 8); + const connectionIdString = connectionIdBinary.toString('hex'); + const bodyLength = headerBuf.readInt32LE(8); + if (bodyLength < 0 || bodyLength > MaxFrameBodyLength) { + // Throwing here is going to bring down the whole process, so this cannot be allowed to happen in real use. + // But it won't happen in real use, because this is only used with our .NET client, which doesn't violate this rule. + throw new Error('Illegal frame body length: ' + bodyLength); + } + + return { connectionIdBinary, connectionIdString, bodyLength }; + } else { + // Not enough bytes are available synchronously, so none were consumed + return null; + } + } + + private _sendFrame(connectionIdBinary: Buffer, data: Buffer, callback?: EndWriteCallback) { + // For all sends other than the last one, only invoke the callback if it failed. + // Also, only invoke the callback at most once. + let hasInvokedCallback = false; + const finalCallback: EndWriteCallback = callback && (error => { + if (!hasInvokedCallback) { + hasInvokedCallback = true; + callback(error); + } + }); + const notFinalCallback: EndWriteCallback = callback && (error => { + if (error) { + finalCallback(error); + } + }); + + // The amount of data we're writing might exceed MaxFrameBodyLength, so split into frames as needed. + // Note that we always send at least one frame, even if it's empty (because that's the close-virtual-connection signal). + // If needed, this could be changed to send frames asynchronously, so that large sends could proceed in parallel + // (though that would involve making a clone of 'data', to avoid the risk of it being mutated during the send). + let bytesSent = 0; + do { + const nextFrameBodyLength = Math.min(MaxFrameBodyLength, data.length - bytesSent); + const isFinalChunk = (bytesSent + nextFrameBodyLength) === data.length; + this._socket.write(connectionIdBinary, notFinalCallback); + this._sendInt32LE(nextFrameBodyLength, notFinalCallback); + this._socket.write(data.slice(bytesSent, bytesSent + nextFrameBodyLength), isFinalChunk ? finalCallback : notFinalCallback); + bytesSent += nextFrameBodyLength; + } while (bytesSent < data.length); + } + + /** + * Sends a number serialized in the correct format for .NET to receive as a System.Int32 + */ + private _sendInt32LE(value: number, callback?: EndWriteCallback) { + const buf = Buffer.alloc(4); + buf.writeInt32LE(value, 0); + this._socket.write(buf, callback); + } + + private _onVirtualConnectionWasClosed(id: string) { + if (this._virtualConnections.hasOwnProperty(id)) { + delete this._virtualConnections[id]; + } + } +} + +interface FrameHeader { + connectionIdBinary: Buffer; + connectionIdString: string; + bodyLength: number; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/tsconfig.json new file mode 100644 index 0000000000..896fc88253 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es3", + "module": "commonjs", + "moduleResolution": "node", + "types": ["node"] + }, + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnection.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnection.cs new file mode 100644 index 0000000000..391b1f760d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnection.cs @@ -0,0 +1,150 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; + +namespace Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections +{ + /// + /// A virtual read/write connection, typically to a remote process. Multiple virtual connections can be + /// multiplexed over a single physical connection (e.g., a named pipe, domain socket, or TCP socket). + /// + internal class VirtualConnection : Stream + { + private readonly static Task CompletedTask = Task.CompletedTask; + private VirtualConnectionClient _host; + private readonly BufferBlock _receivedDataQueue = new BufferBlock(); + private ArraySegment _receivedDataNotYetUsed; + private bool _wasClosedByRemote; + private bool _isDisposed; + + public VirtualConnection(long id, VirtualConnectionClient host) + { + Id = id; + _host = host; + } + + public long Id { get; } + + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return true; } } + + public override long Length + { + get { throw new NotImplementedException(); } + } + + public override long Position + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override void Flush() + { + // We're auto-flushing, so this is a no-op. + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (_wasClosedByRemote) + { + return 0; + } + + var bytesRead = 0; + while (true) + { + // Pull as many applicable bytes as we can out of receivedDataNotYetUsed, then update its offset/length + int bytesToExtract = Math.Min(count - bytesRead, _receivedDataNotYetUsed.Count); + if (bytesToExtract > 0) + { + Buffer.BlockCopy(_receivedDataNotYetUsed.Array, _receivedDataNotYetUsed.Offset, buffer, bytesRead, bytesToExtract); + _receivedDataNotYetUsed = new ArraySegment(_receivedDataNotYetUsed.Array, _receivedDataNotYetUsed.Offset + bytesToExtract, _receivedDataNotYetUsed.Count - bytesToExtract); + bytesRead += bytesToExtract; + } + + // If we've completely filled the output buffer, we're done + if (bytesRead == count) + { + return bytesRead; + } + + // We haven't yet filled the output buffer, so we must have exhausted receivedDataNotYetUsed instead. + // We want to get the next block of data from the underlying queue. + byte[] nextReceivedBlock; + if (bytesRead > 0) + { + if (!_receivedDataQueue.TryReceive(null, out nextReceivedBlock)) + { + // No more data is available synchronously, and we already have some data, so we can stop now + return bytesRead; + } + } + else + { + // Since we don't yet have anything, wait for the underlying source + nextReceivedBlock = await _receivedDataQueue.ReceiveAsync(cancellationToken); + } + + if (nextReceivedBlock.Length == 0) + { + // A zero-length block signals that the remote regards this virtual connection as closed + _wasClosedByRemote = true; + return bytesRead; + } + else + { + // We got some more data, so can continue trying to fill the output buffer + _receivedDataNotYetUsed = new ArraySegment(nextReceivedBlock, 0, nextReceivedBlock.Length); + } + } + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (_wasClosedByRemote) + { + throw new InvalidOperationException("The connection was already closed by the remote party"); + } + + return count > 0 ? _host.WriteAsync(Id, buffer, offset, count, cancellationToken) : CompletedTask; + } + + public override int Read(byte[] buffer, int offset, int count) + { + return ReadAsync(buffer, offset, count, CancellationToken.None).Result; + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + WriteAsync(buffer, offset, count, CancellationToken.None).Wait(); + } + + protected override void Dispose(bool disposing) + { + if (disposing && !_isDisposed) + { + _isDisposed = true; + _host.CloseInnerStream(Id, _wasClosedByRemote); + } + } + + public async Task AddDataToQueue(byte[] data) + { + await _receivedDataQueue.SendAsync(data); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnectionClient.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnectionClient.cs new file mode 100644 index 0000000000..48ec967758 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/VirtualConnections/VirtualConnectionClient.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections +{ + /// + /// A callback that will be invoked if the encounters a read error. + /// + /// + public delegate void VirtualConnectionReadErrorHandler(Exception ex); + + /// + /// Wraps an underlying physical read/write stream (e.g., named pipes, domain sockets, or TCP sockets) and + /// exposes an API for making 'virtual connections', which act as independent read/write streams. + /// Traffic over these virtual connections is multiplexed over the underlying physical stream. This is useful + /// for fast stream-based inter-process communication because it avoids the overhead of opening a new physical + /// connection each time a new communication channel is needed. + /// + internal class VirtualConnectionClient : IDisposable + { + internal const int MaxFrameBodyLength = 16 * 1024; + + public event VirtualConnectionReadErrorHandler OnError; + + private Stream _underlyingTransport; + private Dictionary _activeInnerStreams; + private long _nextInnerStreamId; + private readonly SemaphoreSlim _streamWriterSemaphore = new SemaphoreSlim(1); + private readonly object _readControlLock = new object(); + private Exception _readLoopExitedWithException; + private readonly CancellationTokenSource _disposalCancellatonToken = new CancellationTokenSource(); + private bool _disposedValue = false; + + public VirtualConnectionClient(Stream underlyingTransport) + { + _underlyingTransport = underlyingTransport; + _activeInnerStreams = new Dictionary(); + + RunReadLoop(); + } + + public Stream OpenVirtualConnection() + { + // Improve discoverability of read-loop errors (in case the developer doesn't add an OnError listener) + ThrowIfReadLoopFailed(); + + var id = Interlocked.Increment(ref _nextInnerStreamId); + var newInnerStream = new VirtualConnection(id, this); + lock (_activeInnerStreams) + { + _activeInnerStreams.Add(id, newInnerStream); + } + + return newInnerStream; + } + + // It's async void because nothing waits for it to finish (it continues indefinitely). It signals any errors via + // a separate channel. + private async void RunReadLoop() + { + try + { + while (!_disposalCancellatonToken.IsCancellationRequested) + { + var remoteIsStillConnected = await ProcessNextFrameAsync(); + if (!remoteIsStillConnected) + { + CloseAllActiveStreams(); + } + } + } + catch (Exception ex) + { + // Not all underlying transports correctly honor cancellation tokens. For example, + // DomainSocketStreamTransport's ReadAsync ignores them, so we only know to stop + // the read loop when the underlying stream is disposed and then it throws ObjectDisposedException. + if (!(ex is TaskCanceledException || ex is ObjectDisposedException)) + { + _readLoopExitedWithException = ex; + + var evt = OnError; + if (evt != null) + { + evt(ex); + } + } + } + } + + private async Task ProcessNextFrameAsync() + { + // First read frame header + var frameHeaderBuffer = await ReadExactLength(12); + if (frameHeaderBuffer == null) + { + return false; // Underlying stream was closed + } + + // Parse frame header, then read the frame body + long streamId = BitConverter.ToInt64(frameHeaderBuffer, 0); + int frameBodyLength = BitConverter.ToInt32(frameHeaderBuffer, 8); + if (frameBodyLength < 0 || frameBodyLength > MaxFrameBodyLength) + { + throw new InvalidDataException("Illegal frame length: " + frameBodyLength); + } + + var frameBody = await ReadExactLength(frameBodyLength); + if (frameBody == null) + { + return false; // Underlying stream was closed + } + + // Dispatch the frame to the relevant inner stream + VirtualConnection innerStream; + lock (_activeInnerStreams) + { + _activeInnerStreams.TryGetValue(streamId, out innerStream); + } + + if (innerStream != null) + { + await innerStream.AddDataToQueue(frameBody); + } + + return true; + } + + private async Task ReadExactLength(int lengthToRead) { + byte[] buffer = new byte[lengthToRead]; + var totalBytesRead = 0; + var ct = _disposalCancellatonToken.Token; + while (totalBytesRead < lengthToRead) + { + var chunkLengthRead = await _underlyingTransport.ReadAsync(buffer, totalBytesRead, lengthToRead - totalBytesRead, ct); + if (chunkLengthRead == 0) + { + // Underlying stream was closed + return null; + } + + totalBytesRead += chunkLengthRead; + } + + return buffer; + } + + private void CloseAllActiveStreams() + { + IList innerStreamsCopy; + + // Only hold the lock while cloning the list of inner streams. Release the lock before + // actually disposing them, because each 'dispose' call will try to take another lock + // so it can remove that inner stream from activeInnerStreams. + lock (_activeInnerStreams) + { + innerStreamsCopy = _activeInnerStreams.Values.ToList(); + } + + foreach (var stream in innerStreamsCopy) + { + stream.Dispose(); + } + } + + public void Dispose() + { + if (!_disposedValue) + { + _disposedValue = true; + + _disposalCancellatonToken.Cancel(); // Stops the read loop + CloseAllActiveStreams(); + } + } + + public async Task WriteAsync(long innerStreamId, byte[] data, int offset, int count, CancellationToken cancellationToken) + { + // In case the amount of data to be sent exceeds the max frame length, split it into separate frames + // Note that we always send at least one frame, even if it's empty, because the zero-length frame is the signal to close a virtual connection + // (hence 'do..while' instead of just 'while'). + int bytesWritten = 0; + do { + // Improve discoverability of read-loop errors (in case the developer doesn't add an OnError listener) + ThrowIfReadLoopFailed(); + + // Hold the write lock only for the time taken to send a single frame, not all frames, to allow large sends to be proceed in parallel + await _streamWriterSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + // Write stream ID, then length prefix, then chunk payload, then flush + var nextChunkBodyLength = Math.Min(MaxFrameBodyLength, count - bytesWritten); + await _underlyingTransport.WriteAsync(BitConverter.GetBytes(innerStreamId), 0, 8, cancellationToken).ConfigureAwait(false); + await _underlyingTransport.WriteAsync(BitConverter.GetBytes(nextChunkBodyLength), 0, 4, cancellationToken).ConfigureAwait(false); + + if (nextChunkBodyLength > 0) + { + await _underlyingTransport.WriteAsync(data, offset + bytesWritten, nextChunkBodyLength, cancellationToken).ConfigureAwait(false); + bytesWritten += nextChunkBodyLength; + } + + await _underlyingTransport.FlushAsync(cancellationToken).ConfigureAwait(false); + } + finally + { + _streamWriterSemaphore.Release(); + } + } while (bytesWritten < count); + } + + public void CloseInnerStream(long innerStreamId, bool isAlreadyClosedRemotely) + { + lock (_activeInnerStreams) + { + if (_activeInnerStreams.ContainsKey(innerStreamId)) + { + _activeInnerStreams.Remove(innerStreamId); + } + } + + if (!isAlreadyClosedRemotely) { + // Also notify the remote that this innerstream is closed + WriteAsync(innerStreamId, new byte[0], 0, 0, new CancellationToken()).Wait(); + } + } + + private void ThrowIfReadLoopFailed() + { + if (_readLoopExitedWithException != null) + { + throw new AggregateException("The connection failed - see InnerException for details.", _readLoopExitedWithException); + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/baseline.netcore.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/baseline.netcore.json new file mode 100644 index 0000000000..ad4020174e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/baseline.netcore.json @@ -0,0 +1,109 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.NodeServices.Sockets, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNetCore.NodeServices.Sockets.NodeServicesOptionsExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseSocketHosting", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.AspNetCore.NodeServices.NodeServicesOptions" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections.VirtualConnectionReadErrorHandler", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "BaseType": "System.MulticastDelegate", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Invoke", + "Parameters": [ + { + "Name": "ex", + "Type": "System.Exception" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "BeginInvoke", + "Parameters": [ + { + "Name": "ex", + "Type": "System.Exception" + }, + { + "Name": "callback", + "Type": "System.AsyncCallback" + }, + { + "Name": "object", + "Type": "System.Object" + } + ], + "ReturnType": "System.IAsyncResult", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "EndInvoke", + "Parameters": [ + { + "Name": "result", + "Type": "System.IAsyncResult" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "object", + "Type": "System.Object" + }, + { + "Name": "method", + "Type": "System.IntPtr" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/package-lock.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/package-lock.json new file mode 100644 index 0000000000..13db959cc2 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/package-lock.json @@ -0,0 +1,4244 @@ +{ + "name": "nodeservices.sockets", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.2.tgz", + "integrity": "sha512-pwZnkVyCGJ3LsQ0/3flQK5lCFao4esIzwUVzzk5NvL9vnkEyDhNf4fhHzUMHvyr56gNZywWTS2MR0euabMSz4A==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", + "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", + "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", + "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", + "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", + "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.5.13" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", + "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", + "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", + "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", + "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/ieee754": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", + "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "dev": true, + "requires": { + "ieee754": "^1.1.11" + } + }, + "@webassemblyjs/leb128": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", + "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "dev": true, + "requires": { + "long": "4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + } + }, + "@webassemblyjs/utf8": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", + "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", + "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/helper-wasm-section": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/wast-printer": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", + "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", + "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", + "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", + "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/floating-point-hex-parser": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-code-frame": "1.5.13", + "@webassemblyjs/helper-fsm": "1.5.13", + "long": "^3.2.0", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", + "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "long": "^3.2.0" + } + }, + "acorn": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", + "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "ajv": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz", + "integrity": "sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==", + "dev": true + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.1.tgz", + "integrity": "sha512-e1neqvSt5pSwQcFnYc6yfGuJD2Q4336cdbHs5VeUO0zTkqPbrHMyw2q1r47fpfLWbvIG8H8A6YO3sck7upTV6Q==", + "dev": true, + "requires": { + "chardet": "^0.5.0", + "iconv-lite": "^0.4.22", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules-path": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.0.tgz", + "integrity": "sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", + "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.0", + "figures": "^2.0.0", + "lodash": "^4.17.10", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.1.0", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "neo-async": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz", + "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", + "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", + "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true + }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tapable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", + "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "ts-loader": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-4.5.0.tgz", + "integrity": "sha512-ihgVaSmgrX4crGV4n7yuoHPoCHbDzj9aepCZR9TgIx4SgJ9gdnB6xLHgUBb7bsFM/f0K6x9iXa65KY/Fu1Klkw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^3.1.4", + "semver": "^5.0.1" + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", + "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", + "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "webpack": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.17.1.tgz", + "integrity": "sha512-vdPYogljzWPhFKDj3Gcp01Vqgu7K3IQlybc3XIdKSQHelK1C3eIQuysEUR7MxKJmdandZlQB/9BG2Jb1leJHaw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/wasm-edit": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" + } + }, + "webpack-cli": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.0.tgz", + "integrity": "sha512-p5NeKDtYwjZozUWq6kGNs9w+Gtw/CPvyuXjXn2HMdz8Tie+krjEg8oAtonvIyITZdvpF7XG9xDHwscLr2c+ugQ==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.0.0", + "global-modules-path": "^2.1.0", + "import-local": "^1.0.0", + "inquirer": "^6.0.0", + "interpret": "^1.1.0", + "loader-utils": "^1.1.0", + "supports-color": "^5.4.0", + "v8-compile-cache": "^2.0.0", + "yargs": "^12.0.1" + } + }, + "webpack-sources": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", + "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", + "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/package.json new file mode 100644 index 0000000000..9ef90e3e89 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/package.json @@ -0,0 +1,19 @@ +{ + "name": "nodeservices.sockets", + "version": "1.0.0", + "description": "This is not really an NPM package and will not be published. This file exists only to reference compilation tools.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack --mode production" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "^10.9.2", + "ts-loader": "^4.5.0", + "typescript": "^3.0.1", + "webpack": "^4.17.1", + "webpack-cli": "^3.1.0" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/webpack.config.js b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/webpack.config.js new file mode 100644 index 0000000000..353e1b058a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices.Sockets/webpack.config.js @@ -0,0 +1,24 @@ +const path = require('path'); + +module.exports = { + target: 'node', + resolve: { + extensions: [ '.ts' ] + }, + module: { + rules: [ + { test: /\.ts$/, use: 'ts-loader' }, + ] + }, + entry: { + 'entrypoint-socket': ['./TypeScript/SocketNodeInstanceEntryPoint'], + }, + output: { + libraryTarget: 'commonjs', + path: path.join(__dirname, 'Content', 'Node'), + filename: '[name].js' + }, + optimization: { + minimize: false + } +}; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/.gitignore new file mode 100644 index 0000000000..98edee716e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/node_modules/ +yarn.lock diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesFactory.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesFactory.cs new file mode 100644 index 0000000000..8432158cb9 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesFactory.cs @@ -0,0 +1,25 @@ +using System; + +namespace Microsoft.AspNetCore.NodeServices +{ + /// + /// Supplies INodeServices instances. + /// + public static class NodeServicesFactory + { + /// + /// Create an instance according to the supplied options. + /// + /// Options for creating the instance. + /// An instance. + public static INodeServices CreateNodeServices(NodeServicesOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof (options)); + } + + return new NodeServicesImpl(options.NodeInstanceFactory); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs new file mode 100644 index 0000000000..5b382ed177 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using Microsoft.AspNetCore.NodeServices.HostingModels; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging.Console; + +namespace Microsoft.AspNetCore.NodeServices +{ + /// + /// Describes options used to configure an instance. + /// + public class NodeServicesOptions + { + internal const string TimeoutConfigPropertyName = nameof(InvocationTimeoutMilliseconds); + private const int DefaultInvocationTimeoutMilliseconds = 60 * 1000; + private const string LogCategoryName = "Microsoft.AspNetCore.NodeServices"; + private static readonly string[] DefaultWatchFileExtensions = { ".js", ".jsx", ".ts", ".tsx", ".json", ".html" }; + + /// + /// Creates a new instance of . + /// + /// The . + public NodeServicesOptions(IServiceProvider serviceProvider) + { + if (serviceProvider == null) + { + throw new ArgumentNullException(nameof (serviceProvider)); + } + + EnvironmentVariables = new Dictionary(); + InvocationTimeoutMilliseconds = DefaultInvocationTimeoutMilliseconds; + WatchFileExtensions = (string[])DefaultWatchFileExtensions.Clone(); + + var hostEnv = serviceProvider.GetService(); + if (hostEnv != null) + { + // In an ASP.NET environment, we can use the IHostingEnvironment data to auto-populate a few + // things that you'd otherwise have to specify manually + ProjectPath = hostEnv.ContentRootPath; + EnvironmentVariables["NODE_ENV"] = hostEnv.IsDevelopment() ? "development" : "production"; // De-facto standard values for Node + } + else + { + ProjectPath = Directory.GetCurrentDirectory(); + } + + var applicationLifetime = serviceProvider.GetService(); + if (applicationLifetime != null) + { + ApplicationStoppingToken = applicationLifetime.ApplicationStopping; + } + + // If the DI system gives us a logger, use it. Otherwise, set up a default one. + var loggerFactory = serviceProvider.GetService(); + NodeInstanceOutputLogger = loggerFactory != null + ? loggerFactory.CreateLogger(LogCategoryName) +#pragma warning disable CS0618 // Type or member is obsolete + : new ConsoleLogger(LogCategoryName, null, false); +#pragma warning restore CS0618 + // By default, we use this package's built-in out-of-process-via-HTTP hosting/transport + this.UseHttpHosting(); + } + + /// + /// Specifies how to construct Node.js instances. An encapsulates all details about + /// how Node.js instances are launched and communicated with. A new will be created + /// automatically if the previous instance has terminated (e.g., because a source file changed). + /// + public Func NodeInstanceFactory { get; set; } + + /// + /// If set, overrides the path to the root of your application. This path is used when locating Node.js modules relative to your project. + /// + public string ProjectPath { get; set; } + + /// + /// If set, the Node.js instance should restart when any matching file on disk within your project changes. + /// + public string[] WatchFileExtensions { get; set; } + + /// + /// The Node.js instance's stdout/stderr will be redirected to this . + /// + public ILogger NodeInstanceOutputLogger { get; set; } + + /// + /// If true, the Node.js instance will accept incoming V8 debugger connections (e.g., from node-inspector). + /// + public bool LaunchWithDebugging { get; set; } + + /// + /// If is true, the Node.js instance will listen for V8 debugger connections on this port. + /// + public int DebuggingPort { get; set; } + + /// + /// If set, starts the Node.js instance with the specified environment variables. + /// + public IDictionary EnvironmentVariables { get; set; } + + /// + /// Specifies the maximum duration, in milliseconds, that your .NET code should wait for Node.js RPC calls to return. + /// + public int InvocationTimeoutMilliseconds { get; set; } + + /// + /// A token that indicates when the host application is stopping. + /// + public CancellationToken ApplicationStoppingToken { get; set; } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesServiceCollectionExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesServiceCollectionExtensions.cs new file mode 100644 index 0000000000..f74ae0889c --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesServiceCollectionExtensions.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.AspNetCore.NodeServices; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up NodeServices in an . + /// + public static class NodeServicesServiceCollectionExtensions + { + /// + /// Adds NodeServices support to the . + /// + /// The . + public static void AddNodeServices(this IServiceCollection serviceCollection) + => AddNodeServices(serviceCollection, _ => {}); + + /// + /// Adds NodeServices support to the . + /// + /// The . + /// A callback that will be invoked to populate the . + public static void AddNodeServices(this IServiceCollection serviceCollection, Action setupAction) + { + if (setupAction == null) + { + throw new ArgumentNullException(nameof (setupAction)); + } + + serviceCollection.AddSingleton(typeof(INodeServices), serviceProvider => + { + // First we let NodeServicesOptions take its defaults from the IServiceProvider, + // then we let the developer override those options + var options = new NodeServicesOptions(serviceProvider); + setupAction(options); + + return NodeServicesFactory.CreateNodeServices(options); + }); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js new file mode 100644 index 0000000000..a992d0fe16 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js @@ -0,0 +1,416 @@ +(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 1); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +module.exports = require("path"); + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(2); + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive, +// but simplifies things for the consumer of this module. +__webpack_require__(3); +__webpack_require__(4); +var http = __webpack_require__(5); +var path = __webpack_require__(0); +var ArgsUtil_1 = __webpack_require__(6); +var ExitWhenParentExits_1 = __webpack_require__(7); +// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct +// reference to Node's runtime 'require' function. +var dynamicRequire = eval('require'); +var server = http.createServer(function (req, res) { + readRequestBodyAsJson(req, function (bodyJson) { + var hasSentResult = false; + var callback = function (errorValue, successValue) { + if (!hasSentResult) { + hasSentResult = true; + if (errorValue) { + respondWithError(res, errorValue); + } + else if (typeof successValue !== 'string') { + // Arbitrary object/number/etc - JSON-serialize it + var successValueJson = void 0; + try { + successValueJson = JSON.stringify(successValue); + } + catch (ex) { + // JSON serialization error - pass it back to .NET + respondWithError(res, ex); + return; + } + res.setHeader('Content-Type', 'application/json'); + res.end(successValueJson); + } + else { + // String - can bypass JSON-serialization altogether + res.setHeader('Content-Type', 'text/plain'); + res.end(successValue); + } + } + }; + // Support streamed responses + Object.defineProperty(callback, 'stream', { + enumerable: true, + get: function () { + if (!hasSentResult) { + hasSentResult = true; + res.setHeader('Content-Type', 'application/octet-stream'); + } + return res; + } + }); + try { + var resolvedPath = path.resolve(process.cwd(), bodyJson.moduleName); + var invokedModule = dynamicRequire(resolvedPath); + var func = bodyJson.exportedFunctionName ? invokedModule[bodyJson.exportedFunctionName] : invokedModule; + if (!func) { + throw new Error('The module "' + resolvedPath + '" has no export named "' + bodyJson.exportedFunctionName + '"'); + } + func.apply(null, [callback].concat(bodyJson.args)); + } + catch (synchronousException) { + callback(synchronousException, null); + } + }); +}); +var parsedArgs = ArgsUtil_1.parseArgs(process.argv); +var requestedPortOrZero = parsedArgs.port || 0; // 0 means 'let the OS decide' +server.listen(requestedPortOrZero, 'localhost', function () { + var addressInfo = server.address(); + // Signal to HttpNodeHost which loopback IP address (IPv4 or IPv6) and port it should make its HTTP connections on + console.log('[Microsoft.AspNetCore.NodeServices.HttpNodeHost:Listening on {' + addressInfo.address + '} port ' + addressInfo.port + '\]'); + // Signal to the NodeServices base class that we're ready to accept invocations + console.log('[Microsoft.AspNetCore.NodeServices:Listening]'); +}); +ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true); +function readRequestBodyAsJson(request, callback) { + var requestBodyAsString = ''; + request.on('data', function (chunk) { requestBodyAsString += chunk; }); + request.on('end', function () { callback(JSON.parse(requestBodyAsString)); }); +} +function respondWithError(res, errorValue) { + res.statusCode = 500; + res.end(JSON.stringify({ + errorMessage: errorValue.message || errorValue, + errorDetails: errorValue.stack || null + })); +} + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +var path = __webpack_require__(0); +var startsWith = function (str, prefix) { return str.substring(0, prefix.length) === prefix; }; +var appRootDir = process.cwd(); +function patchedLStat(pathToStatLong, fsReqWrap) { + try { + // If the lstat completes without errors, we don't modify its behavior at all + return origLStat.apply(this, arguments); + } + catch (ex) { + var shouldOverrideError = startsWith(ex.message, 'EPERM') // It's a permissions error + && typeof appRootDirLong === 'string' + && startsWith(appRootDirLong, pathToStatLong) // ... for an ancestor directory + && ex.stack.indexOf('Object.realpathSync ') >= 0; // ... during symlink resolution + if (shouldOverrideError) { + // Fake the result to give the same result as an 'lstat' on the app root dir. + // This stops Node failing to load modules just because it doesn't know whether + // ancestor directories are symlinks or not. If there's a genuine file + // permissions issue, it will still surface later when Node actually + // tries to read the file. + return origLStat.call(this, appRootDir, fsReqWrap); + } + else { + // In any other case, preserve the original error + throw ex; + } + } +} +; +// It's only necessary to apply this workaround on Windows +var appRootDirLong = null; +var origLStat = null; +if (/^win/.test(process.platform)) { + try { + // Get the app's root dir in Node's internal "long" format (e.g., \\?\C:\dir\subdir) + appRootDirLong = path._makeLong(appRootDir); + // Actually apply the patch, being as defensive as possible + var bindingFs = process.binding('fs'); + origLStat = bindingFs.lstat; + if (typeof origLStat === 'function') { + bindingFs.lstat = patchedLStat; + } + } + catch (ex) { + // If some future version of Node throws (e.g., to prevent use of process.binding()), + // don't apply the patch, but still let the application run. + } +} + + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + +// When Node writes to stdout/strerr, we capture that and convert the lines into calls on the +// active .NET ILogger. But by default, stdout/stderr don't have any way of distinguishing +// linebreaks inside log messages from the linebreaks that delimit separate log messages, +// so multiline strings will end up being written to the ILogger as multiple independent +// log messages. This makes them very hard to make sense of, especially when they represent +// something like stack traces. +// +// To fix this, we intercept stdout/stderr writes, and replace internal linebreaks with a +// marker token. When .NET receives the lines, it converts the marker tokens back to regular +// linebreaks within the logged messages. +// +// Note that it's better to do the interception at the stdout/stderr level, rather than at +// the console.log/console.error (etc.) level, because this takes place after any native +// message formatting has taken place (e.g., inserting values for % placeholders). +var findInternalNewlinesRegex = /\n(?!$)/g; +var encodedNewline = '__ns_newline__'; +encodeNewlinesWrittenToStream(process.stdout); +encodeNewlinesWrittenToStream(process.stderr); +function encodeNewlinesWrittenToStream(outputStream) { + var origWriteFunction = outputStream.write; + outputStream.write = function (value) { + // Only interfere with the write if it's definitely a string + if (typeof value === 'string') { + var argsClone = Array.prototype.slice.call(arguments, 0); + argsClone[0] = encodeNewlinesInString(value); + origWriteFunction.apply(this, argsClone); + } + else { + origWriteFunction.apply(this, arguments); + } + }; +} +function encodeNewlinesInString(str) { + return str.replace(findInternalNewlinesRegex, encodedNewline); +} + + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + +module.exports = require("http"); + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +function parseArgs(args) { + // Very simplistic parsing which is sufficient for the cases needed. We don't want to bring in any external + // dependencies (such as an args-parsing library) to this file. + var result = {}; + var currentKey = null; + args.forEach(function (arg) { + if (arg.indexOf('--') === 0) { + var argName = arg.substring(2); + result[argName] = undefined; + currentKey = argName; + } + else if (currentKey) { + result[currentKey] = arg; + currentKey = null; + } + }); + return result; +} +exports.parseArgs = parseArgs; + + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/* +In general, we want the Node child processes to be terminated as soon as the parent .NET processes exit, +because we have no further use for them. If the .NET process shuts down gracefully, it will run its +finalizers, one of which (in OutOfProcessNodeInstance.cs) will kill its associated Node process immediately. + +But if the .NET process is terminated forcefully (e.g., on Linux/OSX with 'kill -9'), then it won't have +any opportunity to shut down its child processes, and by default they will keep running. In this case, it's +up to the child process to detect this has happened and terminate itself. + +There are many possible approaches to detecting when a parent process has exited, most of which behave +differently between Windows and Linux/OS X: + + - On Windows, the parent process can mark its child as being a 'job' that should auto-terminate when + the parent does (http://stackoverflow.com/a/4657392). Not cross-platform. + - The child Node process can get a callback when the parent disconnects (process.on('disconnect', ...)). + But despite http://stackoverflow.com/a/16487966, no callback fires in any case I've tested (Windows / OS X). + - The child Node process can get a callback when its stdin/stdout are disconnected, as described at + http://stackoverflow.com/a/15693934. This works well on OS X, but calling stdout.resume() on Windows + causes the process to terminate prematurely. + - I don't know why, but on Windows, it's enough to invoke process.stdin.resume(). For some reason this causes + the child Node process to exit as soon as the parent one does, but I don't see this documented anywhere. + - You can poll to see if the parent process, or your stdin/stdout connection to it, is gone + - You can directly pass a parent process PID to the child, and then have the child poll to see if it's + still running (e.g., using process.kill(pid, 0), which doesn't kill it but just tests whether it exists, + as per https://nodejs.org/api/process.html#process_process_kill_pid_signal) + - Or, on each poll, you can try writing to process.stdout. If the parent has died, then this will throw. + However I don't see this documented anywhere. It would be nice if you could just poll for whether or not + process.stdout is still connected (without actually writing to it) but I haven't found any property whose + value changes until you actually try to write to it. + +Of these, the only cross-platform approach that is actually documented as a valid strategy is simply polling +to check whether the parent PID is still running. So that's what we do here. +*/ +exports.__esModule = true; +var pollIntervalMs = 1000; +function exitWhenParentExits(parentPid, ignoreSigint) { + setInterval(function () { + if (!processExists(parentPid)) { + // Can't log anything at this point, because out stdout was connected to the parent, + // but the parent is gone. + process.exit(); + } + }, pollIntervalMs); + if (ignoreSigint) { + // Pressing ctrl+c in the terminal sends a SIGINT to all processes in the foreground process tree. + // By default, the Node process would then exit before the .NET process, because ASP.NET implements + // a delayed shutdown to allow ongoing requests to complete. + // + // This is problematic, because if Node exits first, the CopyToAsync code in ConditionalProxyMiddleware + // will experience a read fault, and logs a huge load of errors. Fortunately, since the Node process is + // already set up to shut itself down if it detects the .NET process is terminated, all we have to do is + // ignore the SIGINT. The Node process will then terminate automatically after the .NET process does. + // + // A better solution would be to have WebpackDevMiddleware listen for SIGINT and gracefully close any + // ongoing EventSource connections before letting the Node process exit, independently of the .NET + // process exiting. However, doing this well in general is very nontrivial (see all the discussion at + // https://github.com/nodejs/node/issues/2642). + process.on('SIGINT', function () { + console.log('Received SIGINT. Waiting for .NET process to exit...'); + }); + } +} +exports.exitWhenParentExits = exitWhenParentExits; +function processExists(pid) { + try { + // Sending signal 0 - on all platforms - tests whether the process exists. As long as it doesn't + // throw, that means it does exist. + process.kill(pid, 0); + return true; + } + catch (ex) { + // If the reason for the error is that we don't have permission to ask about this process, + // report that as a separate problem. + if (ex.code === 'EPERM') { + throw new Error("Attempted to check whether process " + pid + " was running, but got a permissions error."); + } + return false; + } +} + + +/***/ }) +/******/ ]))); \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs new file mode 100644 index 0000000000..1466db3eaf --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs @@ -0,0 +1,157 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.NodeServices.HostingModels +{ + /// + /// A specialisation of the OutOfProcessNodeInstance base class that uses HTTP to perform RPC invocations. + /// + /// The Node child process starts an HTTP listener on an arbitrary available port (except where a nonzero + /// port number is specified as a constructor parameter), and signals which port was selected using the same + /// input/output-based mechanism that the base class uses to determine when the child process is ready to + /// accept RPC invocations. + /// + /// + internal class HttpNodeInstance : OutOfProcessNodeInstance + { + private static readonly Regex EndpointMessageRegex = + new Regex(@"^\[Microsoft.AspNetCore.NodeServices.HttpNodeHost:Listening on {(.*?)} port (\d+)\]$"); + + private static readonly JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + TypeNameHandling = TypeNameHandling.None + }; + + private readonly HttpClient _client; + private bool _disposed; + private string _endpoint; + + public HttpNodeInstance(NodeServicesOptions options, int port = 0) + : base( + EmbeddedResourceReader.Read( + typeof(HttpNodeInstance), + "/Content/Node/entrypoint-http.js"), + options.ProjectPath, + options.WatchFileExtensions, + MakeCommandLineOptions(port), + options.ApplicationStoppingToken, + options.NodeInstanceOutputLogger, + options.EnvironmentVariables, + options.InvocationTimeoutMilliseconds, + options.LaunchWithDebugging, + options.DebuggingPort) + { + _client = new HttpClient(); + _client.Timeout = TimeSpan.FromMilliseconds(options.InvocationTimeoutMilliseconds + 1000); + } + + private static string MakeCommandLineOptions(int port) + { + return $"--port {port}"; + } + + protected override async Task InvokeExportAsync( + NodeInvocationInfo invocationInfo, CancellationToken cancellationToken) + { + var payloadJson = JsonConvert.SerializeObject(invocationInfo, jsonSerializerSettings); + var payload = new StringContent(payloadJson, Encoding.UTF8, "application/json"); + var response = await _client.PostAsync(_endpoint, payload, cancellationToken); + + if (!response.IsSuccessStatusCode) + { + // Unfortunately there's no true way to cancel ReadAsStringAsync calls, hence AbandonIfCancelled + var responseJson = await response.Content.ReadAsStringAsync().OrThrowOnCancellation(cancellationToken); + var responseError = JsonConvert.DeserializeObject(responseJson, jsonSerializerSettings); + + throw new NodeInvocationException(responseError.ErrorMessage, responseError.ErrorDetails); + } + + var responseContentType = response.Content.Headers.ContentType; + switch (responseContentType.MediaType) + { + case "text/plain": + // String responses can skip JSON encoding/decoding + if (typeof(T) != typeof(string)) + { + throw new ArgumentException( + "Node module responded with non-JSON string. This cannot be converted to the requested generic type: " + + typeof(T).FullName); + } + + var responseString = await response.Content.ReadAsStringAsync().OrThrowOnCancellation(cancellationToken); + return (T)(object)responseString; + + case "application/json": + var responseJson = await response.Content.ReadAsStringAsync().OrThrowOnCancellation(cancellationToken); + return JsonConvert.DeserializeObject(responseJson, jsonSerializerSettings); + + case "application/octet-stream": + // Streamed responses have to be received as System.IO.Stream instances + if (typeof(T) != typeof(Stream) && typeof(T) != typeof(object)) + { + throw new ArgumentException( + "Node module responded with binary stream. This cannot be converted to the requested generic type: " + + typeof(T).FullName + ". Instead you must use the generic type System.IO.Stream."); + } + + return (T)(object)(await response.Content.ReadAsStreamAsync().OrThrowOnCancellation(cancellationToken)); + + default: + throw new InvalidOperationException("Unexpected response content type: " + responseContentType.MediaType); + } + } + + protected override void OnOutputDataReceived(string outputData) + { + // Watch for "port selected" messages, and when observed, + // store the IP (IPv4/IPv6) and port number + // so we can use it when making HTTP requests. The child process will always send + // one of these messages before it sends a "ready for connections" message. + var match = string.IsNullOrEmpty(_endpoint) ? EndpointMessageRegex.Match(outputData) : null; + if (match != null && match.Success) + { + var port = int.Parse(match.Groups[2].Captures[0].Value); + var resolvedIpAddress = match.Groups[1].Captures[0].Value; + + //IPv6 must be wrapped with [] brackets + resolvedIpAddress = resolvedIpAddress == "::1" ? $"[{resolvedIpAddress}]" : resolvedIpAddress; + _endpoint = $"http://{resolvedIpAddress}:{port}"; + } + else + { + base.OnOutputDataReceived(outputData); + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (!_disposed) + { + if (disposing) + { + _client.Dispose(); + } + + _disposed = true; + } + } + +#pragma warning disable 649 // These properties are populated via JSON deserialization + private class RpcJsonResponse + { + public string ErrorMessage { get; set; } + public string ErrorDetails { get; set; } + } +#pragma warning restore 649 + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/INodeInstance.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/INodeInstance.cs new file mode 100644 index 0000000000..a1ce02146a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/INodeInstance.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices.HostingModels +{ + /// + /// Represents an instance of Node.js to which Remote Procedure Calls (RPC) may be sent. + /// + public interface INodeInstance : IDisposable + { + /// + /// Asynchronously invokes code in the Node.js instance. + /// + /// The JSON-serializable data type that the Node.js code will asynchronously return. + /// A that can be used to cancel the invocation. + /// The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked. + /// If set, specifies the CommonJS export to be invoked. If not set, the module's default CommonJS export itself must be a function to be invoked. + /// Any sequence of JSON-serializable arguments to be passed to the Node.js function. + /// A representing the completion of the RPC call. + Task InvokeExportAsync(CancellationToken cancellationToken, string moduleName, string exportNameOrNull, params object[] args); + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeInvocationException.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeInvocationException.cs new file mode 100644 index 0000000000..dea3c2cc7e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeInvocationException.cs @@ -0,0 +1,55 @@ +using System; + +namespace Microsoft.AspNetCore.NodeServices.HostingModels +{ + /// + /// Represents an exception caused by invoking Node.js code. + /// + public class NodeInvocationException : Exception + { + /// + /// If true, indicates that the invocation failed because the Node.js instance could not be reached. For example, + /// it might have already shut down or previously crashed. + /// + public bool NodeInstanceUnavailable { get; private set; } + + /// + /// If true, indicates that even though the invocation failed because the Node.js instance could not be reached + /// or needs to be restarted, that Node.js instance may remain alive for a period in order to complete any + /// outstanding requests. + /// + public bool AllowConnectionDraining { get; private set;} + + /// + /// Creates a new instance of . + /// + /// A description of the exception. + /// Additional information, such as a Node.js stack trace, representing the exception. + public NodeInvocationException(string message, string details) + : base(message + Environment.NewLine + details) + { + } + + /// + /// Creates a new instance of . + /// + /// A description of the exception. + /// Additional information, such as a Node.js stack trace, representing the exception. + /// Specifies a value for the flag. + /// Specifies a value for the flag. + public NodeInvocationException(string message, string details, bool nodeInstanceUnavailable, bool allowConnectionDraining) + : this(message, details) + { + // Reject a meaningless combination of flags + if (allowConnectionDraining && !nodeInstanceUnavailable) + { + throw new ArgumentException( + $"The '${ nameof(allowConnectionDraining) }' parameter cannot be true " + + $"unless the '${ nameof(nodeInstanceUnavailable) }' parameter is also true."); + } + + NodeInstanceUnavailable = nodeInstanceUnavailable; + AllowConnectionDraining = allowConnectionDraining; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeInvocationInfo.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeInvocationInfo.cs new file mode 100644 index 0000000000..86c427327b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeInvocationInfo.cs @@ -0,0 +1,24 @@ +namespace Microsoft.AspNetCore.NodeServices.HostingModels +{ + /// + /// Describes an RPC call sent from .NET code to Node.js code. + /// + public class NodeInvocationInfo + { + /// + /// Specifies the path to the Node.js module (i.e., .js file) relative to the project root. + /// + public string ModuleName { get; set; } + + /// + /// If set, specifies the name of CommonJS function export to be invoked. + /// If not set, the Node.js module's default export must itself be a function to be invoked. + /// + public string ExportedFunctionName { get; set; } + + /// + /// A sequence of JSON-serializable arguments to be passed to the Node.js function being invoked. + /// + public object[] Args { get; set; } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeServicesOptionsExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeServicesOptionsExtensions.cs new file mode 100644 index 0000000000..2cbe0a0f1c --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/NodeServicesOptionsExtensions.cs @@ -0,0 +1,17 @@ +namespace Microsoft.AspNetCore.NodeServices.HostingModels +{ + /// + /// Extension methods that help with populating a object. + /// + public static class NodeServicesOptionsExtensions + { + /// + /// Configures the service so that it will use out-of-process + /// Node.js instances and perform RPC calls over HTTP. + /// + public static void UseHttpHosting(this NodeServicesOptions options) + { + options.NodeInstanceFactory = () => new HttpNodeInstance(options); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs new file mode 100644 index 0000000000..b75827c452 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/HostingModels/OutOfProcessNodeInstance.cs @@ -0,0 +1,475 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.NodeServices.HostingModels +{ + /// + /// Class responsible for launching a Node child process on the local machine, determining when it is ready to + /// accept invocations, detecting if it dies on its own, and finally terminating it on disposal. + /// + /// This abstract base class uses the input/output streams of the child process to perform a simple handshake + /// to determine when the child process is ready to accept invocations. This is agnostic to the mechanism that + /// derived classes use to actually perform the invocations (e.g., they could use HTTP-RPC, or a binary TCP + /// protocol, or any other RPC-type mechanism). + /// + /// + public abstract class OutOfProcessNodeInstance : INodeInstance + { + /// + /// The to which the Node.js instance's stdout/stderr is being redirected. + /// + protected readonly ILogger OutputLogger; + + private const string ConnectionEstablishedMessage = "[Microsoft.AspNetCore.NodeServices:Listening]"; + private readonly TaskCompletionSource _connectionIsReadySource = new TaskCompletionSource(); + private bool _disposed; + private readonly StringAsTempFile _entryPointScript; + private FileSystemWatcher _fileSystemWatcher; + private int _invocationTimeoutMilliseconds; + private bool _launchWithDebugging; + private readonly Process _nodeProcess; + private int? _nodeDebuggingPort; + private bool _nodeProcessNeedsRestart; + private readonly string[] _watchFileExtensions; + + /// + /// Creates a new instance of . + /// + /// The path to the entry point script that the Node instance should load and execute. + /// The root path of the current project. This is used when resolving Node.js module paths relative to the project root. + /// The filename extensions that should be watched within the project root. The Node instance will automatically shut itself down if any matching file changes. + /// Additional command-line arguments to be passed to the Node.js instance. + /// A token that indicates when the host application is stopping. + /// The to which the Node.js instance's stdout/stderr (and other log information) should be written. + /// Environment variables to be set on the Node.js process. + /// The maximum duration, in milliseconds, to wait for RPC calls to complete. + /// If true, passes a flag to the Node.js process telling it to accept V8 debugger connections. + /// If debugging is enabled, the Node.js process should listen for V8 debugger connections on this port. + public OutOfProcessNodeInstance( + string entryPointScript, + string projectPath, + string[] watchFileExtensions, + string commandLineArguments, + CancellationToken applicationStoppingToken, + ILogger nodeOutputLogger, + IDictionary environmentVars, + int invocationTimeoutMilliseconds, + bool launchWithDebugging, + int debuggingPort) + { + if (nodeOutputLogger == null) + { + throw new ArgumentNullException(nameof(nodeOutputLogger)); + } + + OutputLogger = nodeOutputLogger; + _entryPointScript = new StringAsTempFile(entryPointScript, applicationStoppingToken); + _invocationTimeoutMilliseconds = invocationTimeoutMilliseconds; + _launchWithDebugging = launchWithDebugging; + + var startInfo = PrepareNodeProcessStartInfo(_entryPointScript.FileName, projectPath, commandLineArguments, + environmentVars, _launchWithDebugging, debuggingPort); + _nodeProcess = LaunchNodeProcess(startInfo); + _watchFileExtensions = watchFileExtensions; + _fileSystemWatcher = BeginFileWatcher(projectPath); + ConnectToInputOutputStreams(); + } + + /// + /// Asynchronously invokes code in the Node.js instance. + /// + /// The JSON-serializable data type that the Node.js code will asynchronously return. + /// A that can be used to cancel the invocation. + /// The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked. + /// If set, specifies the CommonJS export to be invoked. If not set, the module's default CommonJS export itself must be a function to be invoked. + /// Any sequence of JSON-serializable arguments to be passed to the Node.js function. + /// A representing the completion of the RPC call. + public async Task InvokeExportAsync( + CancellationToken cancellationToken, string moduleName, string exportNameOrNull, params object[] args) + { + if (_nodeProcess.HasExited || _nodeProcessNeedsRestart) + { + // This special kind of exception triggers a transparent retry - NodeServicesImpl will launch + // a new Node instance and pass the invocation to that one instead. + // Note that if the Node process is listening for debugger connections, then we need it to shut + // down immediately and not stay open for connection draining (because if it did, the new Node + // instance wouldn't able to start, because the old one would still hold the debugging port). + var message = _nodeProcess.HasExited + ? "The Node process has exited" + : "The Node process needs to restart"; + throw new NodeInvocationException( + message, + details: null, + nodeInstanceUnavailable: true, + allowConnectionDraining: !_launchWithDebugging); + } + + // Construct a new cancellation token that combines the supplied token with the configured invocation + // timeout. Technically we could avoid wrapping the cancellationToken if no timeout is configured, + // but that's not really a major use case, since timeouts are enabled by default. + using (var timeoutSource = new CancellationTokenSource()) + using (var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutSource.Token)) + { + if (_invocationTimeoutMilliseconds > 0) + { + timeoutSource.CancelAfter(_invocationTimeoutMilliseconds); + } + + // By overwriting the supplied cancellation token, we ensure that it isn't accidentally used + // below. We only want to pass through the token that respects timeouts. + cancellationToken = combinedCancellationTokenSource.Token; + var connectionDidSucceed = false; + + try + { + // Wait until the connection is established. This will throw if the connection fails to initialize, + // or if cancellation is requested first. Note that we can't really cancel the "establishing connection" + // task because that's shared with all callers, but we can stop waiting for it if this call is cancelled. + await _connectionIsReadySource.Task.OrThrowOnCancellation(cancellationToken); + connectionDidSucceed = true; + + return await InvokeExportAsync(new NodeInvocationInfo + { + ModuleName = moduleName, + ExportedFunctionName = exportNameOrNull, + Args = args + }, cancellationToken); + } + catch (TaskCanceledException) + { + if (timeoutSource.IsCancellationRequested) + { + // It was very common for developers to report 'TaskCanceledException' when encountering almost any + // trouble when using NodeServices. Now we have a default invocation timeout, and attempt to give + // a more descriptive exception message if it happens. + if (!connectionDidSucceed) + { + // This is very unlikely, but for debugging, it's still useful to differentiate it from the + // case below. + throw new NodeInvocationException( + $"Attempt to connect to Node timed out after {_invocationTimeoutMilliseconds}ms.", + string.Empty); + } + else + { + // Developers encounter this fairly often (if their Node code fails without invoking the callback, + // all that the .NET side knows is that the invocation eventually times out). Previously, this surfaced + // as a TaskCanceledException, but this led to a lot of issue reports. Now we throw the following + // descriptive error. + throw new NodeInvocationException( + $"The Node invocation timed out after {_invocationTimeoutMilliseconds}ms.", + $"You can change the timeout duration by setting the {NodeServicesOptions.TimeoutConfigPropertyName} " + + $"property on {nameof(NodeServicesOptions)}.\n\n" + + "The first debugging step is to ensure that your Node.js function always invokes the supplied " + + "callback (or throws an exception synchronously), even if it encounters an error. Otherwise, " + + "the .NET code has no way to know that it is finished or has failed." + ); + } + } + else + { + throw; + } + } + } + } + + /// + /// Disposes this instance. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Asynchronously invokes code in the Node.js instance. + /// + /// The JSON-serializable data type that the Node.js code will asynchronously return. + /// Specifies the Node.js function to be invoked and arguments to be passed to it. + /// A that can be used to cancel the invocation. + /// A representing the completion of the RPC call. + protected abstract Task InvokeExportAsync( + NodeInvocationInfo invocationInfo, + CancellationToken cancellationToken); + + /// + /// Configures a instance describing how to launch the Node.js process. + /// + /// The entrypoint JavaScript file that the Node.js process should execute. + /// The root path of the project. This is used when locating Node.js modules relative to the project root. + /// Command-line arguments to be passed to the Node.js process. + /// Environment variables to be set on the Node.js process. + /// If true, passes a flag to the Node.js process telling it to accept V8 Inspector connections. + /// If debugging is enabled, the Node.js process should listen for V8 Inspector connections on this port. + /// + protected virtual ProcessStartInfo PrepareNodeProcessStartInfo( + string entryPointFilename, string projectPath, string commandLineArguments, + IDictionary environmentVars, bool launchWithDebugging, int debuggingPort) + { + // This method is virtual, as it provides a way to override the NODE_PATH or the path to node.exe + string debuggingArgs; + if (launchWithDebugging) + { + debuggingArgs = debuggingPort != default(int) ? $"--inspect={debuggingPort} " : "--inspect "; + _nodeDebuggingPort = debuggingPort; + } + else + { + debuggingArgs = string.Empty; + } + + var thisProcessPid = Process.GetCurrentProcess().Id; + var startInfo = new ProcessStartInfo("node") + { + Arguments = $"{debuggingArgs}\"{entryPointFilename}\" --parentPid {thisProcessPid} {commandLineArguments ?? string.Empty}", + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + WorkingDirectory = projectPath + }; + + // Append environment vars + if (environmentVars != null) + { + foreach (var envVarKey in environmentVars.Keys) + { + var envVarValue = environmentVars[envVarKey]; + if (envVarValue != null) + { + SetEnvironmentVariable(startInfo, envVarKey, envVarValue); + } + } + } + + // Append projectPath to NODE_PATH so it can locate node_modules + var existingNodePath = Environment.GetEnvironmentVariable("NODE_PATH") ?? string.Empty; + if (existingNodePath != string.Empty) + { + existingNodePath += Path.PathSeparator; + } + + var nodePathValue = existingNodePath + Path.Combine(projectPath, "node_modules"); + SetEnvironmentVariable(startInfo, "NODE_PATH", nodePathValue); + + return startInfo; + } + + /// + /// Virtual method invoked whenever the Node.js process emits a line to its stdout. + /// + /// The line emitted to the Node.js process's stdout. + protected virtual void OnOutputDataReceived(string outputData) + { + OutputLogger.LogInformation(outputData); + } + + /// + /// Virtual method invoked whenever the Node.js process emits a line to its stderr. + /// + /// The line emitted to the Node.js process's stderr. + protected virtual void OnErrorDataReceived(string errorData) + { + OutputLogger.LogError(errorData); + } + + /// + /// Disposes the instance. + /// + /// True if the object is disposing or false if it is finalizing. + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _entryPointScript.Dispose(); + EnsureFileSystemWatcherIsDisposed(); + } + + // Make sure the Node process is finished + // TODO: Is there a more graceful way to end it? Or does this still let it perform any cleanup? + if (_nodeProcess != null && !_nodeProcess.HasExited) + { + _nodeProcess.Kill(); + } + + _disposed = true; + } + } + + private void EnsureFileSystemWatcherIsDisposed() + { + if (_fileSystemWatcher != null) + { + _fileSystemWatcher.Dispose(); + _fileSystemWatcher = null; + } + } + + private static void SetEnvironmentVariable(ProcessStartInfo startInfo, string name, string value) + { + startInfo.Environment[name] = value; + } + + private static Process LaunchNodeProcess(ProcessStartInfo startInfo) + { + try { + var process = Process.Start(startInfo); + + // On Mac at least, a killed child process is left open as a zombie until the parent + // captures its exit code. We don't need the exit code for this process, and don't want + // to use process.WaitForExit() explicitly (we'd have to block the thread until it really + // has exited), but we don't want to leave zombies lying around either. It's sufficient + // to use process.EnableRaisingEvents so that .NET will grab the exit code and let the + // zombie be cleaned away without having to block our thread. + process.EnableRaisingEvents = true; + + return process; + } catch (Exception ex) { + var message = "Failed to start Node process. To resolve this:.\n\n" + + "[1] Ensure that Node.js is installed and can be found in one of the PATH directories.\n" + + $" Current PATH enviroment variable is: { Environment.GetEnvironmentVariable("PATH") }\n" + + " Make sure the Node executable is in one of those directories, or update your PATH.\n\n" + + "[2] See the InnerException for further details of the cause."; + throw new InvalidOperationException(message, ex); + } + } + + private static string UnencodeNewlines(string str) + { + if (str != null) + { + // The token here needs to match the const in OverrideStdOutputs.ts. + // See the comment there for why we're doing this. + str = str.Replace("__ns_newline__", Environment.NewLine); + } + + return str; + } + + private void ConnectToInputOutputStreams() + { + var initializationIsCompleted = false; + + _nodeProcess.OutputDataReceived += (sender, evt) => + { + if (evt.Data == ConnectionEstablishedMessage && !initializationIsCompleted) + { + _connectionIsReadySource.SetResult(null); + initializationIsCompleted = true; + } + else if (evt.Data != null) + { + OnOutputDataReceived(UnencodeNewlines(evt.Data)); + } + }; + + _nodeProcess.ErrorDataReceived += (sender, evt) => + { + if (evt.Data != null) + { + if (_launchWithDebugging && IsDebuggerMessage(evt.Data)) + { + OutputLogger.LogWarning(evt.Data); + } + else + { + OnErrorDataReceived(UnencodeNewlines(evt.Data)); + } + } + }; + + _nodeProcess.BeginOutputReadLine(); + _nodeProcess.BeginErrorReadLine(); + } + + private static bool IsDebuggerMessage(string message) + { + return message.StartsWith("Debugger attached", StringComparison.Ordinal) || + message.StartsWith("Debugger listening ", StringComparison.Ordinal) || + message.StartsWith("To start debugging", StringComparison.Ordinal) || + message.Equals("Warning: This is an experimental feature and could change at any time.", StringComparison.Ordinal) || + message.Equals("For help see https://nodejs.org/en/docs/inspector", StringComparison.Ordinal) || + message.Contains("chrome-devtools:"); + } + + private FileSystemWatcher BeginFileWatcher(string rootDir) + { + if (_watchFileExtensions == null || _watchFileExtensions.Length == 0) + { + // Nothing to watch + return null; + } + + var watcher = new FileSystemWatcher(rootDir) + { + IncludeSubdirectories = true, + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName + }; + watcher.Changed += OnFileChanged; + watcher.Created += OnFileChanged; + watcher.Deleted += OnFileChanged; + watcher.Renamed += OnFileRenamed; + watcher.EnableRaisingEvents = true; + return watcher; + } + + private void OnFileChanged(object source, FileSystemEventArgs e) + { + if (IsFilenameBeingWatched(e.FullPath)) + { + RestartDueToFileChange(e.FullPath); + } + } + + private void OnFileRenamed(object source, RenamedEventArgs e) + { + if (IsFilenameBeingWatched(e.OldFullPath) || IsFilenameBeingWatched(e.FullPath)) + { + RestartDueToFileChange(e.OldFullPath); + } + } + + private bool IsFilenameBeingWatched(string fullPath) + { + if (string.IsNullOrEmpty(fullPath)) + { + return false; + } + else + { + var actualExtension = Path.GetExtension(fullPath) ?? string.Empty; + return _watchFileExtensions.Any(actualExtension.Equals); + } + } + + private void RestartDueToFileChange(string fullPath) + { + OutputLogger.LogInformation($"Node will restart because file changed: {fullPath}"); + + _nodeProcessNeedsRestart = true; + + // There's no need to watch for any more changes, since we're already restarting, and if the + // restart takes some time (e.g., due to connection draining), we could end up getting duplicate + // notifications. + EnsureFileSystemWatcherIsDisposed(); + } + + /// + /// Implements the finalization part of the IDisposable pattern by calling Dispose(false). + /// + ~OutOfProcessNodeInstance() + { + Dispose(false); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/INodeServices.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/INodeServices.cs new file mode 100644 index 0000000000..2a1ce1aa77 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/INodeServices.cs @@ -0,0 +1,54 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices +{ + /// + /// Represents the ability to invoke code in a Node.js environment. Although the underlying Node.js instance + /// might change over time (e.g., the process might be restarted), the instance + /// will remain constant. + /// + public interface INodeServices : IDisposable + { + /// + /// Asynchronously invokes code in the Node.js instance. + /// + /// The JSON-serializable data type that the Node.js code will asynchronously return. + /// The path to the Node.js module (i.e., JavaScript file) relative to your project root whose default CommonJS export is the function to be invoked. + /// Any sequence of JSON-serializable arguments to be passed to the Node.js function. + /// A representing the completion of the RPC call. + Task InvokeAsync(string moduleName, params object[] args); + + /// + /// Asynchronously invokes code in the Node.js instance. + /// + /// The JSON-serializable data type that the Node.js code will asynchronously return. + /// A that can be used to cancel the invocation. + /// The path to the Node.js module (i.e., JavaScript file) relative to your project root whose default CommonJS export is the function to be invoked. + /// Any sequence of JSON-serializable arguments to be passed to the Node.js function. + /// A representing the completion of the RPC call. + Task InvokeAsync(CancellationToken cancellationToken, string moduleName, params object[] args); + + /// + /// Asynchronously invokes code in the Node.js instance. + /// + /// The JSON-serializable data type that the Node.js code will asynchronously return. + /// The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked. + /// Specifies the CommonJS export to be invoked. + /// Any sequence of JSON-serializable arguments to be passed to the Node.js function. + /// A representing the completion of the RPC call. + Task InvokeExportAsync(string moduleName, string exportedFunctionName, params object[] args); + + /// + /// Asynchronously invokes code in the Node.js instance. + /// + /// The JSON-serializable data type that the Node.js code will asynchronously return. + /// A that can be used to cancel the invocation. + /// The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked. + /// Specifies the CommonJS export to be invoked. + /// Any sequence of JSON-serializable arguments to be passed to the Node.js function. + /// A representing the completion of the RPC call. + Task InvokeExportAsync(CancellationToken cancellationToken, string moduleName, string exportedFunctionName, params object[] args); + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj new file mode 100644 index 0000000000..6d64797f2f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj @@ -0,0 +1,24 @@ + + + + Invoke Node.js modules at runtime in ASP.NET Core applications. + netstandard2.0 + + + + + + + + + + + + + + + + + + + diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/NodeServicesImpl.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/NodeServicesImpl.cs new file mode 100644 index 0000000000..d1cf92404f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/NodeServicesImpl.cs @@ -0,0 +1,165 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.NodeServices.HostingModels; + +namespace Microsoft.AspNetCore.NodeServices +{ + /// + /// Default implementation of INodeServices. This is the primary API surface through which developers + /// make use of this package. It provides simple "InvokeAsync" methods that dispatch calls to the + /// correct Node instance, creating and destroying those instances as needed. + /// + /// If a Node instance dies (or none was yet created), this class takes care of creating a new one. + /// If a Node instance signals that it needs to be restarted (e.g., because a file changed), then this + /// class will create a new instance and dispatch future calls to it, while keeping the old instance + /// alive for a defined period so that any in-flight RPC calls can complete. This latter feature is + /// analogous to the "connection draining" feature implemented by HTTP load balancers. + /// + /// + internal class NodeServicesImpl : INodeServices + { + private static TimeSpan ConnectionDrainingTimespan = TimeSpan.FromSeconds(15); + private Func _nodeInstanceFactory; + private INodeInstance _currentNodeInstance; + private object _currentNodeInstanceAccessLock = new object(); + private Exception _instanceDelayedDisposalException; + + internal NodeServicesImpl(Func nodeInstanceFactory) + { + _nodeInstanceFactory = nodeInstanceFactory; + } + + public Task InvokeAsync(string moduleName, params object[] args) + { + return InvokeExportAsync(moduleName, null, args); + } + + public Task InvokeAsync(CancellationToken cancellationToken, string moduleName, params object[] args) + { + return InvokeExportAsync(cancellationToken, moduleName, null, args); + } + + public Task InvokeExportAsync(string moduleName, string exportedFunctionName, params object[] args) + { + return InvokeExportWithPossibleRetryAsync(moduleName, exportedFunctionName, args, /* allowRetry */ true, CancellationToken.None); + } + + public Task InvokeExportAsync(CancellationToken cancellationToken, string moduleName, string exportedFunctionName, params object[] args) + { + return InvokeExportWithPossibleRetryAsync(moduleName, exportedFunctionName, args, /* allowRetry */ true, cancellationToken); + } + + private async Task InvokeExportWithPossibleRetryAsync(string moduleName, string exportedFunctionName, object[] args, bool allowRetry, CancellationToken cancellationToken) + { + ThrowAnyOutstandingDelayedDisposalException(); + var nodeInstance = GetOrCreateCurrentNodeInstance(); + + try + { + return await nodeInstance.InvokeExportAsync(cancellationToken, moduleName, exportedFunctionName, args); + } + catch (NodeInvocationException ex) + { + // If the Node instance can't complete the invocation because it needs to restart (e.g., because the underlying + // Node process has exited, or a file it depends on has changed), then we make one attempt to restart transparently. + if (allowRetry && ex.NodeInstanceUnavailable) + { + // Perform the retry after clearing away the old instance + // Since we disposal is delayed even though the node instance is replaced immediately, this produces the + // "connection draining" feature whereby in-flight RPC calls are given a certain period to complete. + lock (_currentNodeInstanceAccessLock) + { + if (_currentNodeInstance == nodeInstance) + { + var disposalDelay = ex.AllowConnectionDraining ? ConnectionDrainingTimespan : TimeSpan.Zero; + DisposeNodeInstance(_currentNodeInstance, disposalDelay); + _currentNodeInstance = null; + } + } + + // One the next call, don't allow retries, because we could get into an infinite retry loop, or a long retry + // loop that masks an underlying problem. A newly-created Node instance should be able to accept invocations, + // or something more serious must be wrong. + return await InvokeExportWithPossibleRetryAsync(moduleName, exportedFunctionName, args, /* allowRetry */ false, cancellationToken); + } + else + { + throw; + } + } + } + + public void Dispose() + { + lock (_currentNodeInstanceAccessLock) + { + if (_currentNodeInstance != null) + { + DisposeNodeInstance(_currentNodeInstance, delay: TimeSpan.Zero); + _currentNodeInstance = null; + } + } + } + + private void DisposeNodeInstance(INodeInstance nodeInstance, TimeSpan delay) + { + if (delay == TimeSpan.Zero) + { + nodeInstance.Dispose(); + } + else + { + Task.Run(async () => { + try + { + await Task.Delay(delay); + nodeInstance.Dispose(); + } + catch(Exception ex) + { + // Nothing's waiting for the delayed disposal task, so any exceptions in it would + // by default just get ignored. To make these discoverable, capture them here so + // they can be rethrown to the next caller to InvokeExportAsync. + _instanceDelayedDisposalException = ex; + } + }); + } + } + + private void ThrowAnyOutstandingDelayedDisposalException() + { + if (_instanceDelayedDisposalException != null) + { + var ex = _instanceDelayedDisposalException; + _instanceDelayedDisposalException = null; + throw new AggregateException( + "A previous attempt to dispose a Node instance failed. See InnerException for details.", + ex); + } + } + + private INodeInstance GetOrCreateCurrentNodeInstance() + { + var instance = _currentNodeInstance; + if (instance == null) + { + lock (_currentNodeInstanceAccessLock) + { + instance = _currentNodeInstance; + if (instance == null) + { + instance = _currentNodeInstance = CreateNewNodeInstance(); + } + } + } + + return instance; + } + + private INodeInstance CreateNewNodeInstance() + { + return _nodeInstanceFactory(); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/README.md b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/README.md new file mode 100644 index 0000000000..c314db36d0 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/README.md @@ -0,0 +1,363 @@ +# Microsoft.AspNetCore.NodeServices + +This NuGet package provides a fast and robust way to invoke Node.js code from a .NET application (typically ASP.NET Core web apps). You can use this whenever you want to use Node/NPM-supplied functionality at runtime in ASP.NET. For example, + + * Executing arbitrary JavaScript + * Runtime integration with JavaScript build or packaging tools, e.g., transpiling code via Babel + * Using of NPM modules for image resizing, audio compression, language recognition, etc. + * Calling third-party services that supply Node-based APIs but don't yet ship native .NET ones + +It is the underlying mechanism supporting the following packages: + + * [`Microsoft.AspNetCore.SpaServices`](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices) - builds on NodeServices, adding functionality commonly used in Single Page Applications, such as server-side prerendering, webpack middleware, and integration between server-side and client-side routing. + * [`Microsoft.AspNetCore.AngularServices`](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.AngularServices) and [`Microsoft.AspNetCore.ReactServices`](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.ReactServices) - these build on `SpaServices`, adding helpers specific to Angular and React, such as cache priming and integrating server-side and client-side validation + +### Requirements + +* [Node.js](https://nodejs.org/en/) + * To test this is installed and can be found, run `node -v` on a command line + * Note: If you're deploying to an Azure web site, you don't need to do anything here - Node is already installed and available in the server environments +* [.NET](https://dot.net) + * For .NET Core (e.g., ASP.NET Core apps), you need at least 1.0 RC2 + * For .NET Framework, you need at least version 4.5.1. + +### Installation + +For .NET Core apps: + + * Add `Microsoft.AspNetCore.NodeServices` to the dependencies list in your `project.json` file + * Run `dotnet restore` (or if you use Visual Studio, just wait a moment - it will restore dependencies automatically) + +For .NET Framework apps: + + * `nuget install Microsoft.AspNetCore.NodeServices` + +### Do you just want to build an ASP.NET Core app with Angular / React / Knockout / etc.? + +In that case, you don't need to use NodeServices directly (or install it manually). You can either: + +* **Recommended:** Use the `aspnetcore-spa` Yeoman generator to get a ready-to-go starting point using your choice of client-side framework. [Instructions here.](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/) +* Or set up your ASP.NET Core and client-side Angular/React/KO/etc. app manually, and then use the [`Microsoft.AspNetCore.SpaServices`](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices) package to add features like server-side prerendering or Webpack middleware. But really, at least try using the `aspnetcore-spa` generator first. + +# Simple usage example + +## For ASP.NET Core apps + +.NET Core has a built-in dependency injection (DI) system. NodeServices is designed to work with this, so you don't have to manage the creation or disposal of instances. + +Enable NodeServices in your application by first adding the following to your `ConfigureServices` method in `Startup.cs`: + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + // ... all your existing configuration is here ... + + // Enable Node Services + services.AddNodeServices(); +} +``` + +Now you can receive an instance of `NodeServices` as an action method parameter to any MVC action, and then use it to make calls into Node.js code, e.g.: + +```csharp +public async Task MyAction([FromServices] INodeServices nodeServices) +{ + var result = await nodeServices.InvokeAsync("./addNumbers", 1, 2); + return Content("1 + 2 = " + result); +} +``` + +Of course, you also need to supply the Node.js code you want to invoke. Create a file called `addNumbers.js` at the root of your ASP.NET Core application, and add the following code: + +```javascript +module.exports = function (callback, first, second) { + var result = first + second; + callback(/* error */ null, result); +}; +``` + +As you can see, the exported JavaScript function will receive the arguments you pass from .NET (as long as they are JSON-serializable), along with a Node-style callback you can use to send back a result or error when you are ready. + +When the `InvokeAsync` method receives the result back from Node, the result will be JSON-deserialized to whatever generic type you specified when calling `InvokeAsync` (e.g., above, that type is `int`). If `InvokeAsync` receives an error from your Node code, it will throw an exception describing that error. + +If you want to put `addNumber.js` inside a subfolder rather than the root of your app, then also amend the path in the `_nodeServices.Invoke` call to match that path. + +## For non-ASP.NET apps + +In other types of .NET Core app, where you don't have ASP.NET supplying an `IServiceCollection` to you, you'll need to instantiate your own DI container. For example, add a reference to the .NET package `Microsoft.Extensions.DependencyInjection`, and then you can construct an `IServiceCollection`, then register NodeServices as usual: + +```csharp +var services = new ServiceCollection(); +services.AddNodeServices(options => { + // Set any properties that you want on 'options' here +}); +``` + +Now you can ask it to supply the shared `INodeServices` instance: + +```csharp +var serviceProvider = services.BuildServiceProvider(); +var nodeServices = serviceProvider.GetRequiredService(); +``` + +Or, if you want to obtain a separate (non-shared) `INodeServices` instance: + +```csharp +var options = new NodeServicesOptions(serviceProvider) { /* Assign/override any other options here */ }; +var nodeServices = NodeServicesFactory.CreateNodeServices(options); +``` + +Besides this, the usage is the same as described for ASP.NET above, so you can now call `nodeServices.InvokeAsync(...)` etc. + +You can dispose the `nodeServices` object whenever you are done with it (and it will shut down the associated Node.js instance), but because these instances are expensive to create, you should whenever possible retain and reuse instances. Don't dispose the shared instance returned from `serviceProvider.GetRequiredService` (except perhaps if you know your application is shutting down, although .NET's finalizers will dispose it anyway if the shutdown is graceful). + +NodeServices instances are thread-safe - you can call `InvokeAsync` simultaneously from multiple threads. Also, they are smart enough to detect if the associated Node instance has died and will automatically start a new Node instance if needed. + +# API Reference + +### AddNodeServices + +**Signatures:** + +```csharp +AddNodeServices() +AddNodeServices(Action setupAction) +``` + +This is an extension method on `IServiceCollection`. It registers NodeServices with ASP.NET Core's DI system. Typically you should call this from the `ConfigureServices` method in your `Startup.cs` file. + +To access this extension method, you'll need to add the following namespace import to the top of your file, if it isn't already there: + +```csharp +using Microsoft.Extensions.DependencyInjection; +``` + +**Examples** + +Using default options: + +```csharp +services.AddNodeServices(); +``` + +Or, specifying options: + +```csharp +services.AddNodeServices(options => +{ + options.WatchFileExtensions = new[] { ".coffee", ".sass" }; + // ... etc. - see other properties below +}); +``` + +**Parameters** + + * `setupAction` - type: `Action` + * Optional. If not specified, defaults will be used. + * Properties on `NodeServicesOptions`: + * `HostingModel` - an `NodeHostingModel` enum value. See: [hosting models](#hosting-models) + * `ProjectPath` - if specified, controls the working directory used when launching Node instances. This affects, for example, the location that `require` statements resolve relative paths against. If not specified, your application root directory is used. + * `WatchFileExtensions` - if specified, the launched Node instance will watch for changes to any files with these extensions, and auto-restarts when any are changed. The default array includes `.js`, `.jsx`, `.ts`, `.tsx`, `.json`, and `.html`. + +**Return type**: None. But once you've done this, you can get `NodeServices` instances out of ASP.NET's DI system. Typically it will be a singleton instance. + +### CreateNodeServices + +**Signature:** + +```csharp +CreateNodeServices(NodeServicesOptions options) +``` + +Supplies a new (non-shared) instance of `NodeServices`. + +**Example** + +```csharp +var options = new NodeServicesOptions(serviceProvider); // Obtains default options from DI config +var nodeServices = NodeServicesFactory.CreateNodeServices(options); +``` + +**Parameters** + * `options` - type: `NodeServicesOptions`. + * Configures the returned `NodeServices` instance. + * Properties: + * `HostingModel` - an `NodeHostingModel` enum value. See: [hosting models](#hosting-models) + * `ProjectPath` - if specified, controls the working directory used when launching Node instances. This affects, for example, the location that `require` statements resolve relative paths against. If not specified, your application root directory is used. + * `WatchFileExtensions` - if specified, the launched Node instance will watch for changes to any files with these extension, and auto-restarts when any are changed. + +**Return type:** `NodeServices` + +If you create a `NodeServices` instance this way, you can also dispose it (call `nodeServiceInstance.Dispose();`) and it will shut down the associated Node instance. But because these instances are expensive to create, you should whenever possible retain and reuse your `NodeServices` object. They are thread-safe - you can call `nodeServiceInstance.InvokeAsync(...)` simultaneously from multiple threads. + +### InvokeAsync<T> + +**Signature:** + +```csharp +InvokeAsync(string moduleName, params object[] args) +``` + +Asynchronously calls a JavaScript function and returns the result, or throws an exception if the result was an error. + +**Example 1: Getting a JSON-serializable object from Node (the most common use case)** + +```csharp +var result = await myNodeServicesInstance.InvokeAsync( + "./Node/transpile", + pathOfSomeFileToBeTranspiled); +``` + +... where `TranspilerResult` might be defined as follows: + +```csharp +public class TranspilerResult +{ + public string Code { get; set; } + public string[] Warnings { get; set; } +} +``` + +... and the corresponding JavaScript module (in `Node/transpile.js`) could be implemented as follows: + +```javascript +module.exports = function (callback, filePath) { + // Invoke some external transpiler (e.g., an NPM module) then: + callback(null, { + code: theTranspiledCodeAsAString, + warnings: someArrayOfStrings + }); +}; +``` + +**Example 2: Getting a stream of binary data from Node** + +```csharp +var imageStream = await myNodeServicesInstance.InvokeAsync( + "./Node/resizeImage", + fullImagePath, + width, + height); + +// In an MVC action method, you can pipe the result to the response as follows +return File(imageStream, someContentType); +``` + +... where the corresponding JavaScript module (in `Node/resizeImage.js`) could be implemented as follows: + +```javascript +var sharp = require('sharp'); // A popular image manipulation package on NPM + +module.exports = function(result, physicalPath, maxWidth, maxHeight) { + // Invoke the 'sharp' NPM module, and have it pipe the resulting image data back to .NET + sharp(physicalPath) + .resize(maxWidth || null, maxHeight || null) + .pipe(result.stream); +} +``` + +There's a working image resizing example following this approach [here](https://github.com/aspnet/JavaScriptServices/tree/dev/samples/misc/NodeServicesExamples) - see the [C# code](https://github.com/aspnet/JavaScriptServices/blob/dev/samples/misc/NodeServicesExamples/Controllers/ResizeImage.cs) and the [JavaScript code](https://github.com/aspnet/JavaScriptServices/blob/dev/samples/misc/NodeServicesExamples/Node/resizeImage.js). + +**Parameters** + +* `moduleName` - type: `string` + * The name of a JavaScript module that Node.js must be able to resolve by calling `require(moduleName)`. This can be a relative path such as `"./Some/Directory/mymodule"`. If you don't specify the `.js` filename extension, Node.js will infer it anyway. +* `params` + * Any set of JSON-serializable objects you want to pass to the exported JavaScript function + +**Return type:** `T`, which must be: + + * A JSON-serializable .NET type, if your JavaScript code uses the `callback(error, result)` pattern to return an object, as in example 1 above + * Or, the type `System.IO.Stream`, if your JavaScript code writes data to the `result.stream` object (which is a [Node `Duplex` stream](https://nodejs.org/api/stream.html#stream_class_stream_duplex)), as in example 2 above + +### InvokeExportAsync<T> + +**Signature** + +```csharp +InvokeExportAsync(string moduleName, string exportName, params object[] args) +``` + +This is exactly the same as `InvokeAsync`, except that it also takes an `exportName` parameter. You can use this if you want your JavaScript module to export more than one function. + +**Example** + +```csharp +var someString = await myNodeServicesInstance.InvokeExportAsync( + "./Node/myNodeApis", + "getMeAString"); + +var someStringInFrench = await myNodeServicesInstance.InvokeExportAsync( + "./Node/myNodeApis", + "convertLanguage" + someString, + "fr-FR"); +``` + +... where the corresponding JavaScript module (in `Node/myNodeApis.js`) could be implemented as follows: + +```javascript +module.exports = { + + getMeAString: function (callback) { + callback(null, 'Here is a string'); + }, + + convertLanguage: function (callback, sourceString, targetLanguage) { + // Implementation detail left as an exercise for the reader + doMachineTranslation(sourceString, targetLanguage, function(error, result) { + callback(error, result); + }); + } + +}; +``` + +**Parameters, return type, etc.** For all other details, see the docs for [`InvokeAsync`](#invokeasynct) + +## Hosting models + +NodeServices has a pluggable hosting/transport mechanism, because it is an abstraction over various possible ways to invoke Node.js from .NET. This allows more high-level facilities (e.g., for Angular prerendering) to be agnostic to the details of launching Node and communicating with it - those high-level facilities can just trust that *somehow* we can invoke code in Node for them. + +Using this abstraction, we could run Node inside the .NET process, in a separate process on the same machine, or even on a different machine altogether. At the time of writing, all the built-in hosting mechanisms work by launching Node as a separate process on the same machine as your .NET code. + +**What about Edge.js?** + +[Edge.js](http://tjanczuk.github.io/edge/#/) hosts Node.js inside a .NET process, or vice-versa, and lets you interoperate between the two. + +NodeServices is not meant to compete with Edge.js. Instead, NodeServices is an abstraction over all possible ways to invoke Node from .NET. Eventually we may offer an in-process Node hosting mechanism via Edge.js, without you needing to change your higher-level code. This can be done when Edge.js supports hosting Node in cross-platform .NET Core processes ([discussion](https://github.com/tjanczuk/edge/issues/279)). + +**What about VroomJS?** + +People have asked about using [VroomJS](https://github.com/fogzot/vroomjs) as a hosting mechanism. We don't currently plan to implement that, because Vroom only supplies a V8 runtime environment, not a complete Node environment. The difference is that, with a true Node environment, *all* NPM modules and Node code will work exactly as expected, whereas in a Vroom environment, code will only work if it doesn't use any Node primitives, which rules out large portions of the NPM landscape. + +### Built-in hosting models + +Normally, you can just use the default hosting model, and not worry about it. But if you have some special requirements, you can write your own hosting model, or reference a package that supplies one. + +For example, you could use the 'socket' hosting model. It performs RPC between .NET and Node.js using a fast, low-level binary channel rather than the default HTTP transport. To do this, first install the NuGet package `Microsoft.AspNetCore.NodeServices.Sockets`. Then, at the top of your `Startup.cs` file, add: + +```csharp +using Microsoft.AspNetCore.NodeServices.Sockets; +``` + +...then in your `Startup.cs` file's `ConfigureServices` method, you can configure: + +```csharp +services.AddNodeServices(options => { + options.UseSocketHosting(); +}); +``` + +Now when you run your application, it will use the socket-based hosting and transport mechanism. In the past, the socket transport was faster than HTTP, but since .NET Core 1.1 improved the performance of `HttpClient` there isn't really any speed difference any more, so there's no longer any significant advantage to using `Microsoft.AspNetCore.NodeServices.Sockets`. + +### Custom hosting models + +If you implement a custom hosting model (by implementing `INodeInstance`), then you can cause it to be used by populating `NodeInstanceFactory` on your options: + +```csharp +services.AddNodeServices(options => +{ + options.NodeInstanceFactory = () => new MyCustomNodeInstance(); +}); +``` diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts new file mode 100644 index 0000000000..bd2af22a28 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts @@ -0,0 +1,97 @@ +// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive, +// but simplifies things for the consumer of this module. +import './Util/PatchModuleResolutionLStat'; +import './Util/OverrideStdOutputs'; +import * as http from 'http'; +import * as path from 'path'; +import { parseArgs } from './Util/ArgsUtil'; +import { exitWhenParentExits } from './Util/ExitWhenParentExits'; +import { AddressInfo } from 'net'; + +// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct +// reference to Node's runtime 'require' function. +const dynamicRequire: (name: string) => any = eval('require'); + +const server = http.createServer((req, res) => { + readRequestBodyAsJson(req, bodyJson => { + let hasSentResult = false; + const callback = (errorValue, successValue) => { + if (!hasSentResult) { + hasSentResult = true; + if (errorValue) { + respondWithError(res, errorValue); + } else if (typeof successValue !== 'string') { + // Arbitrary object/number/etc - JSON-serialize it + let successValueJson: string; + try { + successValueJson = JSON.stringify(successValue); + } catch (ex) { + // JSON serialization error - pass it back to .NET + respondWithError(res, ex); + return; + } + res.setHeader('Content-Type', 'application/json'); + res.end(successValueJson); + } else { + // String - can bypass JSON-serialization altogether + res.setHeader('Content-Type', 'text/plain'); + res.end(successValue); + } + } + }; + + // Support streamed responses + Object.defineProperty(callback, 'stream', { + enumerable: true, + get: function() { + if (!hasSentResult) { + hasSentResult = true; + res.setHeader('Content-Type', 'application/octet-stream'); + } + + return res; + } + }); + + try { + const resolvedPath = path.resolve(process.cwd(), bodyJson.moduleName); + const invokedModule = dynamicRequire(resolvedPath); + const func = bodyJson.exportedFunctionName ? invokedModule[bodyJson.exportedFunctionName] : invokedModule; + if (!func) { + throw new Error('The module "' + resolvedPath + '" has no export named "' + bodyJson.exportedFunctionName + '"'); + } + + func.apply(null, [callback].concat(bodyJson.args)); + } catch (synchronousException) { + callback(synchronousException, null); + } + }); +}); + +const parsedArgs = parseArgs(process.argv); +const requestedPortOrZero = parsedArgs.port || 0; // 0 means 'let the OS decide' +server.listen(requestedPortOrZero, 'localhost', function () { + const addressInfo = server.address() as AddressInfo; + + // Signal to HttpNodeHost which loopback IP address (IPv4 or IPv6) and port it should make its HTTP connections on + console.log('[Microsoft.AspNetCore.NodeServices.HttpNodeHost:Listening on {' + addressInfo.address + '} port ' + addressInfo.port + '\]'); + + // Signal to the NodeServices base class that we're ready to accept invocations + console.log('[Microsoft.AspNetCore.NodeServices:Listening]'); +}); + +exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true); + +function readRequestBodyAsJson(request, callback) { + let requestBodyAsString = ''; + request.on('data', chunk => { requestBodyAsString += chunk; }); + request.on('end', () => { callback(JSON.parse(requestBodyAsString)); }); +} + +function respondWithError(res: http.ServerResponse, errorValue: any) { + res.statusCode = 500; + res.end(JSON.stringify({ + errorMessage: errorValue.message || errorValue, + errorDetails: errorValue.stack || null + })); +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/ArgsUtil.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/ArgsUtil.ts new file mode 100644 index 0000000000..cfafb5d2c6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/ArgsUtil.ts @@ -0,0 +1,18 @@ +export function parseArgs(args: string[]): any { + // Very simplistic parsing which is sufficient for the cases needed. We don't want to bring in any external + // dependencies (such as an args-parsing library) to this file. + const result = {}; + let currentKey = null; + args.forEach(arg => { + if (arg.indexOf('--') === 0) { + const argName = arg.substring(2); + result[argName] = undefined; + currentKey = argName; + } else if (currentKey) { + result[currentKey] = arg; + currentKey = null; + } + }); + + return result; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/ExitWhenParentExits.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/ExitWhenParentExits.ts new file mode 100644 index 0000000000..43c865f352 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/ExitWhenParentExits.ts @@ -0,0 +1,81 @@ +/* +In general, we want the Node child processes to be terminated as soon as the parent .NET processes exit, +because we have no further use for them. If the .NET process shuts down gracefully, it will run its +finalizers, one of which (in OutOfProcessNodeInstance.cs) will kill its associated Node process immediately. + +But if the .NET process is terminated forcefully (e.g., on Linux/OSX with 'kill -9'), then it won't have +any opportunity to shut down its child processes, and by default they will keep running. In this case, it's +up to the child process to detect this has happened and terminate itself. + +There are many possible approaches to detecting when a parent process has exited, most of which behave +differently between Windows and Linux/OS X: + + - On Windows, the parent process can mark its child as being a 'job' that should auto-terminate when + the parent does (http://stackoverflow.com/a/4657392). Not cross-platform. + - The child Node process can get a callback when the parent disconnects (process.on('disconnect', ...)). + But despite http://stackoverflow.com/a/16487966, no callback fires in any case I've tested (Windows / OS X). + - The child Node process can get a callback when its stdin/stdout are disconnected, as described at + http://stackoverflow.com/a/15693934. This works well on OS X, but calling stdout.resume() on Windows + causes the process to terminate prematurely. + - I don't know why, but on Windows, it's enough to invoke process.stdin.resume(). For some reason this causes + the child Node process to exit as soon as the parent one does, but I don't see this documented anywhere. + - You can poll to see if the parent process, or your stdin/stdout connection to it, is gone + - You can directly pass a parent process PID to the child, and then have the child poll to see if it's + still running (e.g., using process.kill(pid, 0), which doesn't kill it but just tests whether it exists, + as per https://nodejs.org/api/process.html#process_process_kill_pid_signal) + - Or, on each poll, you can try writing to process.stdout. If the parent has died, then this will throw. + However I don't see this documented anywhere. It would be nice if you could just poll for whether or not + process.stdout is still connected (without actually writing to it) but I haven't found any property whose + value changes until you actually try to write to it. + +Of these, the only cross-platform approach that is actually documented as a valid strategy is simply polling +to check whether the parent PID is still running. So that's what we do here. +*/ + +const pollIntervalMs = 1000; + +export function exitWhenParentExits(parentPid: number, ignoreSigint: boolean) { + setInterval(() => { + if (!processExists(parentPid)) { + // Can't log anything at this point, because out stdout was connected to the parent, + // but the parent is gone. + process.exit(); + } + }, pollIntervalMs); + + if (ignoreSigint) { + // Pressing ctrl+c in the terminal sends a SIGINT to all processes in the foreground process tree. + // By default, the Node process would then exit before the .NET process, because ASP.NET implements + // a delayed shutdown to allow ongoing requests to complete. + // + // This is problematic, because if Node exits first, the CopyToAsync code in ConditionalProxyMiddleware + // will experience a read fault, and logs a huge load of errors. Fortunately, since the Node process is + // already set up to shut itself down if it detects the .NET process is terminated, all we have to do is + // ignore the SIGINT. The Node process will then terminate automatically after the .NET process does. + // + // A better solution would be to have WebpackDevMiddleware listen for SIGINT and gracefully close any + // ongoing EventSource connections before letting the Node process exit, independently of the .NET + // process exiting. However, doing this well in general is very nontrivial (see all the discussion at + // https://github.com/nodejs/node/issues/2642). + process.on('SIGINT', () => { + console.log('Received SIGINT. Waiting for .NET process to exit...'); + }); + } +} + +function processExists(pid: number) { + try { + // Sending signal 0 - on all platforms - tests whether the process exists. As long as it doesn't + // throw, that means it does exist. + process.kill(pid, 0); + return true; + } catch (ex) { + // If the reason for the error is that we don't have permission to ask about this process, + // report that as a separate problem. + if (ex.code === 'EPERM') { + throw new Error(`Attempted to check whether process ${pid} was running, but got a permissions error.`); + } + + return false; + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/OverrideStdOutputs.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/OverrideStdOutputs.ts new file mode 100644 index 0000000000..01e4bc6de3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/OverrideStdOutputs.ts @@ -0,0 +1,37 @@ +// When Node writes to stdout/strerr, we capture that and convert the lines into calls on the +// active .NET ILogger. But by default, stdout/stderr don't have any way of distinguishing +// linebreaks inside log messages from the linebreaks that delimit separate log messages, +// so multiline strings will end up being written to the ILogger as multiple independent +// log messages. This makes them very hard to make sense of, especially when they represent +// something like stack traces. +// +// To fix this, we intercept stdout/stderr writes, and replace internal linebreaks with a +// marker token. When .NET receives the lines, it converts the marker tokens back to regular +// linebreaks within the logged messages. +// +// Note that it's better to do the interception at the stdout/stderr level, rather than at +// the console.log/console.error (etc.) level, because this takes place after any native +// message formatting has taken place (e.g., inserting values for % placeholders). +const findInternalNewlinesRegex = /\n(?!$)/g; +const encodedNewline = '__ns_newline__'; + +encodeNewlinesWrittenToStream(process.stdout); +encodeNewlinesWrittenToStream(process.stderr); + +function encodeNewlinesWrittenToStream(outputStream: NodeJS.WritableStream) { + const origWriteFunction = outputStream.write; + outputStream.write = function (value: any) { + // Only interfere with the write if it's definitely a string + if (typeof value === 'string') { + const argsClone = Array.prototype.slice.call(arguments, 0); + argsClone[0] = encodeNewlinesInString(value); + origWriteFunction.apply(this, argsClone); + } else { + origWriteFunction.apply(this, arguments); + } + }; +} + +function encodeNewlinesInString(str: string): string { + return str.replace(findInternalNewlinesRegex, encodedNewline); +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/PatchModuleResolutionLStat.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/PatchModuleResolutionLStat.ts new file mode 100644 index 0000000000..de5185ff8b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/PatchModuleResolutionLStat.ts @@ -0,0 +1,48 @@ +import * as path from 'path'; +const startsWith = (str: string, prefix: string) => str.substring(0, prefix.length) === prefix; +const appRootDir = process.cwd(); + +function patchedLStat(pathToStatLong: string, fsReqWrap?: any) { + try { + // If the lstat completes without errors, we don't modify its behavior at all + return origLStat.apply(this, arguments); + } catch(ex) { + const shouldOverrideError = + startsWith(ex.message, 'EPERM') // It's a permissions error + && typeof appRootDirLong === 'string' + && startsWith(appRootDirLong, pathToStatLong) // ... for an ancestor directory + && ex.stack.indexOf('Object.realpathSync ') >= 0; // ... during symlink resolution + + if (shouldOverrideError) { + // Fake the result to give the same result as an 'lstat' on the app root dir. + // This stops Node failing to load modules just because it doesn't know whether + // ancestor directories are symlinks or not. If there's a genuine file + // permissions issue, it will still surface later when Node actually + // tries to read the file. + return origLStat.call(this, appRootDir, fsReqWrap); + } else { + // In any other case, preserve the original error + throw ex; + } + } +}; + +// It's only necessary to apply this workaround on Windows +let appRootDirLong: string = null; +let origLStat: Function = null; +if (/^win/.test(process.platform)) { + try { + // Get the app's root dir in Node's internal "long" format (e.g., \\?\C:\dir\subdir) + appRootDirLong = (path as any)._makeLong(appRootDir); + + // Actually apply the patch, being as defensive as possible + const bindingFs = (process as any).binding('fs'); + origLStat = bindingFs.lstat; + if (typeof origLStat === 'function') { + bindingFs.lstat = patchedLStat; + } + } catch(ex) { + // If some future version of Node throws (e.g., to prevent use of process.binding()), + // don't apply the patch, but still let the application run. + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/tsconfig.json new file mode 100644 index 0000000000..896fc88253 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/TypeScript/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es3", + "module": "commonjs", + "moduleResolution": "node", + "types": ["node"] + }, + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/EmbeddedResourceReader.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/EmbeddedResourceReader.cs new file mode 100644 index 0000000000..053182669b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/EmbeddedResourceReader.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using System.Reflection; + +namespace Microsoft.AspNetCore.NodeServices +{ + /// + /// Contains methods for reading embedded resources. + /// + public static class EmbeddedResourceReader + { + /// + /// Reads the specified embedded resource from a given assembly. + /// + /// Any in the assembly whose resource is to be read. + /// The path of the resource to be read. + /// The contents of the resource. + public static string Read(Type assemblyContainingType, string path) + { + var asm = assemblyContainingType.GetTypeInfo().Assembly; + var embeddedResourceName = asm.GetName().Name + path.Replace("/", "."); + + using (var stream = asm.GetManifestResourceStream(embeddedResourceName)) + using (var sr = new StreamReader(stream)) + { + return sr.ReadToEnd(); + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs new file mode 100644 index 0000000000..c4aea7ff2d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using System.Threading; + +namespace Microsoft.AspNetCore.NodeServices +{ + /// + /// Makes it easier to pass script files to Node in a way that's sure to clean up after the process exits. + /// + public sealed class StringAsTempFile : IDisposable + { + private bool _disposedValue; + private bool _hasDeletedTempFile; + private object _fileDeletionLock = new object(); + private IDisposable _applicationLifetimeRegistration; + + /// + /// Create a new instance of . + /// + /// The contents of the temporary file to be created. + /// A token that indicates when the host application is stopping. + public StringAsTempFile(string content, CancellationToken applicationStoppingToken) + { + FileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + File.WriteAllText(FileName, content); + + // Because .NET finalizers don't reliably run when the process is terminating, also + // add event handlers for other shutdown scenarios. + _applicationLifetimeRegistration = applicationStoppingToken.Register(EnsureTempFileDeleted); + } + + /// + /// Specifies the filename of the temporary file. + /// + public string FileName { get; } + + /// + /// Disposes the instance and deletes the associated temporary file. + /// + public void Dispose() + { + DisposeImpl(true); + GC.SuppressFinalize(this); + } + + private void DisposeImpl(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // Dispose managed state + _applicationLifetimeRegistration.Dispose(); + } + + EnsureTempFileDeleted(); + + _disposedValue = true; + } + } + + private void EnsureTempFileDeleted() + { + lock (_fileDeletionLock) + { + if (!_hasDeletedTempFile) + { + File.Delete(FileName); + _hasDeletedTempFile = true; + } + } + } + + /// + /// Implements the finalization part of the IDisposable pattern by calling Dispose(false). + /// + ~StringAsTempFile() + { + DisposeImpl(false); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/TaskExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/TaskExtensions.cs new file mode 100644 index 0000000000..75cfdb1604 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/Util/TaskExtensions.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices +{ + internal static class TaskExtensions + { + public static Task OrThrowOnCancellation(this Task task, CancellationToken cancellationToken) + { + return task.IsCompleted + ? task // If the task is already completed, no need to wrap it in a further layer of task + : task.ContinueWith( + _ => {}, // If the task completes, allow execution to continue + cancellationToken, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + + public static Task OrThrowOnCancellation(this Task task, CancellationToken cancellationToken) + { + return task.IsCompleted + ? task // If the task is already completed, no need to wrap it in a further layer of task + : task.ContinueWith( + t => t.Result, // If the task completes, pass through its result + cancellationToken, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/baseline.netcore.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/baseline.netcore.json new file mode 100644 index 0000000000..c91f911c9a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/baseline.netcore.json @@ -0,0 +1,935 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.NodeServices, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.NodeServicesServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddNodeServices", + "Parameters": [ + { + "Name": "serviceCollection", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddNodeServices", + "Parameters": [ + { + "Name": "serviceCollection", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "setupAction", + "Type": "System.Action" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.NodeServicesFactory", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CreateNodeServices", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.AspNetCore.NodeServices.NodeServicesOptions" + } + ], + "ReturnType": "Microsoft.AspNetCore.NodeServices.INodeServices", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.NodeServicesOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_NodeInstanceFactory", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NodeInstanceFactory", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProjectPath", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProjectPath", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_WatchFileExtensions", + "Parameters": [], + "ReturnType": "System.String[]", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_WatchFileExtensions", + "Parameters": [ + { + "Name": "value", + "Type": "System.String[]" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NodeInstanceOutputLogger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NodeInstanceOutputLogger", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LaunchWithDebugging", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_LaunchWithDebugging", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_DebuggingPort", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_DebuggingPort", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_EnvironmentVariables", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IDictionary", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_EnvironmentVariables", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.IDictionary" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_InvocationTimeoutMilliseconds", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_InvocationTimeoutMilliseconds", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ApplicationStoppingToken", + "Parameters": [], + "ReturnType": "System.Threading.CancellationToken", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ApplicationStoppingToken", + "Parameters": [ + { + "Name": "value", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "serviceProvider", + "Type": "System.IServiceProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.INodeServices", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "InvokeAsync", + "Parameters": [ + { + "Name": "moduleName", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "InvokeAsync", + "Parameters": [ + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + }, + { + "Name": "moduleName", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "InvokeExportAsync", + "Parameters": [ + { + "Name": "moduleName", + "Type": "System.String" + }, + { + "Name": "exportedFunctionName", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "InvokeExportAsync", + "Parameters": [ + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + }, + { + "Name": "moduleName", + "Type": "System.String" + }, + { + "Name": "exportedFunctionName", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.EmbeddedResourceReader", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Read", + "Parameters": [ + { + "Name": "assemblyContainingType", + "Type": "System.Type" + }, + { + "Name": "path", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.StringAsTempFile", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_FileName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Finalize", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "content", + "Type": "System.String" + }, + { + "Name": "applicationStoppingToken", + "Type": "System.Threading.CancellationToken" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "InvokeExportAsync", + "Parameters": [ + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + }, + { + "Name": "moduleName", + "Type": "System.String" + }, + { + "Name": "exportNameOrNull", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.HostingModels.NodeInvocationException", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "System.Exception", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_NodeInstanceUnavailable", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AllowConnectionDraining", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "message", + "Type": "System.String" + }, + { + "Name": "details", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "message", + "Type": "System.String" + }, + { + "Name": "details", + "Type": "System.String" + }, + { + "Name": "nodeInstanceUnavailable", + "Type": "System.Boolean" + }, + { + "Name": "allowConnectionDraining", + "Type": "System.Boolean" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.HostingModels.NodeInvocationInfo", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ModuleName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ModuleName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExportedFunctionName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ExportedFunctionName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Args", + "Parameters": [], + "ReturnType": "System.Object[]", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Args", + "Parameters": [ + { + "Name": "value", + "Type": "System.Object[]" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.HostingModels.NodeServicesOptionsExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseHttpHosting", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.AspNetCore.NodeServices.NodeServicesOptions" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.HostingModels.OutOfProcessNodeInstance", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvokeExportAsync", + "Parameters": [ + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + }, + { + "Name": "moduleName", + "Type": "System.String" + }, + { + "Name": "exportNameOrNull", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "InvokeExportAsync", + "Parameters": [ + { + "Name": "invocationInfo", + "Type": "Microsoft.AspNetCore.NodeServices.HostingModels.NodeInvocationInfo" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "PrepareNodeProcessStartInfo", + "Parameters": [ + { + "Name": "entryPointFilename", + "Type": "System.String" + }, + { + "Name": "projectPath", + "Type": "System.String" + }, + { + "Name": "commandLineArguments", + "Type": "System.String" + }, + { + "Name": "environmentVars", + "Type": "System.Collections.Generic.IDictionary" + }, + { + "Name": "launchWithDebugging", + "Type": "System.Boolean" + }, + { + "Name": "debuggingPort", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Diagnostics.ProcessStartInfo", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "OnOutputDataReceived", + "Parameters": [ + { + "Name": "outputData", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "OnErrorDataReceived", + "Parameters": [ + { + "Name": "errorData", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [ + { + "Name": "disposing", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Finalize", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "entryPointScript", + "Type": "System.String" + }, + { + "Name": "projectPath", + "Type": "System.String" + }, + { + "Name": "watchFileExtensions", + "Type": "System.String[]" + }, + { + "Name": "commandLineArguments", + "Type": "System.String" + }, + { + "Name": "applicationStoppingToken", + "Type": "System.Threading.CancellationToken" + }, + { + "Name": "nodeOutputLogger", + "Type": "Microsoft.Extensions.Logging.ILogger" + }, + { + "Name": "environmentVars", + "Type": "System.Collections.Generic.IDictionary" + }, + { + "Name": "invocationTimeoutMilliseconds", + "Type": "System.Int32" + }, + { + "Name": "launchWithDebugging", + "Type": "System.Boolean" + }, + { + "Name": "debuggingPort", + "Type": "System.Int32" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "OutputLogger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "ReadOnly": true, + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/package-lock.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/package-lock.json new file mode 100644 index 0000000000..98fc0542b3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/package-lock.json @@ -0,0 +1,4244 @@ +{ + "name": "nodeservices", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.2.tgz", + "integrity": "sha512-pwZnkVyCGJ3LsQ0/3flQK5lCFao4esIzwUVzzk5NvL9vnkEyDhNf4fhHzUMHvyr56gNZywWTS2MR0euabMSz4A==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", + "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", + "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", + "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", + "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", + "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.5.13" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", + "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", + "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", + "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", + "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/ieee754": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", + "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "dev": true, + "requires": { + "ieee754": "^1.1.11" + } + }, + "@webassemblyjs/leb128": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", + "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "dev": true, + "requires": { + "long": "4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + } + }, + "@webassemblyjs/utf8": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", + "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", + "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/helper-wasm-section": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/wast-printer": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", + "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", + "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", + "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", + "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/floating-point-hex-parser": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-code-frame": "1.5.13", + "@webassemblyjs/helper-fsm": "1.5.13", + "long": "^3.2.0", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", + "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "long": "^3.2.0" + } + }, + "acorn": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", + "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "ajv": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz", + "integrity": "sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==", + "dev": true + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.1.tgz", + "integrity": "sha512-e1neqvSt5pSwQcFnYc6yfGuJD2Q4336cdbHs5VeUO0zTkqPbrHMyw2q1r47fpfLWbvIG8H8A6YO3sck7upTV6Q==", + "dev": true, + "requires": { + "chardet": "^0.5.0", + "iconv-lite": "^0.4.22", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules-path": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.0.tgz", + "integrity": "sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", + "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.0", + "figures": "^2.0.0", + "lodash": "^4.17.10", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.1.0", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "neo-async": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz", + "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", + "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", + "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true + }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tapable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", + "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "ts-loader": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-4.5.0.tgz", + "integrity": "sha512-ihgVaSmgrX4crGV4n7yuoHPoCHbDzj9aepCZR9TgIx4SgJ9gdnB6xLHgUBb7bsFM/f0K6x9iXa65KY/Fu1Klkw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^3.1.4", + "semver": "^5.0.1" + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", + "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", + "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "webpack": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.17.1.tgz", + "integrity": "sha512-vdPYogljzWPhFKDj3Gcp01Vqgu7K3IQlybc3XIdKSQHelK1C3eIQuysEUR7MxKJmdandZlQB/9BG2Jb1leJHaw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/wasm-edit": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" + } + }, + "webpack-cli": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.0.tgz", + "integrity": "sha512-p5NeKDtYwjZozUWq6kGNs9w+Gtw/CPvyuXjXn2HMdz8Tie+krjEg8oAtonvIyITZdvpF7XG9xDHwscLr2c+ugQ==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.0.0", + "global-modules-path": "^2.1.0", + "import-local": "^1.0.0", + "inquirer": "^6.0.0", + "interpret": "^1.1.0", + "loader-utils": "^1.1.0", + "supports-color": "^5.4.0", + "v8-compile-cache": "^2.0.0", + "yargs": "^12.0.1" + } + }, + "webpack-sources": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", + "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", + "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/package.json new file mode 100644 index 0000000000..f234bc6063 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/package.json @@ -0,0 +1,19 @@ +{ + "name": "nodeservices", + "version": "1.0.0", + "description": "This is not really an NPM package and will not be published. This file exists only to reference compilation tools.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack --mode production" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "^10.9.2", + "ts-loader": "^4.5.0", + "typescript": "^3.0.1", + "webpack": "^4.17.1", + "webpack-cli": "^3.1.0" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/webpack.config.js b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/webpack.config.js new file mode 100644 index 0000000000..af32593fb3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.NodeServices/webpack.config.js @@ -0,0 +1,24 @@ +const path = require('path'); + +module.exports = { + target: 'node', + resolve: { + extensions: [ '.ts' ] + }, + module: { + rules: [ + { test: /\.ts$/, use: 'ts-loader' }, + ] + }, + entry: { + 'entrypoint-http': ['./TypeScript/HttpNodeInstanceEntryPoint'] + }, + output: { + libraryTarget: 'commonjs', + path: path.join(__dirname, 'Content', 'Node'), + filename: '[name].js' + }, + optimization: { + minimize: false + } +}; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliBuilder.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliBuilder.cs new file mode 100644 index 0000000000..61dedd350a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliBuilder.cs @@ -0,0 +1,84 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.NodeServices.Npm; +using Microsoft.AspNetCore.NodeServices.Util; +using Microsoft.AspNetCore.SpaServices.Prerendering; +using Microsoft.AspNetCore.SpaServices.Util; +using System; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.SpaServices.AngularCli +{ + /// + /// Provides an implementation of that can build + /// an Angular application by invoking the Angular CLI. + /// + public class AngularCliBuilder : ISpaPrerendererBuilder + { + private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine + + private readonly string _npmScriptName; + + /// + /// Constructs an instance of . + /// + /// The name of the script in your package.json file that builds the server-side bundle for your Angular application. + public AngularCliBuilder(string npmScript) + { + if (string.IsNullOrEmpty(npmScript)) + { + throw new ArgumentException("Cannot be null or empty.", nameof(npmScript)); + } + + _npmScriptName = npmScript; + } + + /// + public async Task Build(ISpaBuilder spaBuilder) + { + var sourcePath = spaBuilder.Options.SourcePath; + if (string.IsNullOrEmpty(sourcePath)) + { + throw new InvalidOperationException($"To use {nameof(AngularCliBuilder)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}."); + } + + var logger = LoggerFinder.GetOrCreateLogger( + spaBuilder.ApplicationBuilder, + nameof(AngularCliBuilder)); + var npmScriptRunner = new NpmScriptRunner( + sourcePath, + _npmScriptName, + "--watch", + null); + npmScriptRunner.AttachToLogger(logger); + + using (var stdOutReader = new EventedStreamStringReader(npmScriptRunner.StdOut)) + using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr)) + { + try + { + await npmScriptRunner.StdOut.WaitForMatch( + new Regex("Date", RegexOptions.None, RegexMatchTimeout)); + } + catch (EndOfStreamException ex) + { + throw new InvalidOperationException( + $"The NPM script '{_npmScriptName}' exited without indicating success.\n" + + $"Output was: {stdOutReader.ReadAsString()}\n" + + $"Error output was: {stdErrReader.ReadAsString()}", ex); + } + catch (OperationCanceledException ex) + { + throw new InvalidOperationException( + $"The NPM script '{_npmScriptName}' timed out without indicating success. " + + $"Output was: {stdOutReader.ReadAsString()}\n" + + $"Error output was: {stdErrReader.ReadAsString()}", ex); + } + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddleware.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddleware.cs new file mode 100644 index 0000000000..9090f7738b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddleware.cs @@ -0,0 +1,145 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.NodeServices.Npm; +using Microsoft.AspNetCore.NodeServices.Util; +using Microsoft.AspNetCore.SpaServices.Util; +using System; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Threading; +using System.Net.Http; +using Microsoft.AspNetCore.SpaServices.Extensions.Util; + +namespace Microsoft.AspNetCore.SpaServices.AngularCli +{ + internal static class AngularCliMiddleware + { + private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices"; + private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine + + public static void Attach( + ISpaBuilder spaBuilder, + string npmScriptName) + { + var sourcePath = spaBuilder.Options.SourcePath; + if (string.IsNullOrEmpty(sourcePath)) + { + throw new ArgumentException("Cannot be null or empty", nameof(sourcePath)); + } + + if (string.IsNullOrEmpty(npmScriptName)) + { + throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName)); + } + + // Start Angular CLI and attach to middleware pipeline + var appBuilder = spaBuilder.ApplicationBuilder; + var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName); + var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, npmScriptName, logger); + + // Everything we proxy is hardcoded to target http://localhost because: + // - the requests are always from the local machine (we're not accepting remote + // requests that go directly to the Angular CLI middleware server) + // - given that, there's no reason to use https, and we couldn't even if we + // wanted to, because in general the Angular CLI server has no certificate + var targetUriTask = angularCliServerInfoTask.ContinueWith( + task => new UriBuilder("http", "localhost", task.Result.Port).Uri); + + SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () => + { + // On each request, we create a separate startup task with its own timeout. That way, even if + // the first request times out, subsequent requests could still work. + var timeout = spaBuilder.Options.StartupTimeout; + return targetUriTask.WithTimeout(timeout, + $"The Angular CLI process did not start listening for requests " + + $"within the timeout period of {timeout.Seconds} seconds. " + + $"Check the log output for error information."); + }); + } + + private static async Task StartAngularCliServerAsync( + string sourcePath, string npmScriptName, ILogger logger) + { + var portNumber = TcpPortFinder.FindAvailablePort(); + logger.LogInformation($"Starting @angular/cli on port {portNumber}..."); + + var npmScriptRunner = new NpmScriptRunner( + sourcePath, npmScriptName, $"--port {portNumber}", null); + npmScriptRunner.AttachToLogger(logger); + + Match openBrowserLine; + using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr)) + { + try + { + openBrowserLine = await npmScriptRunner.StdOut.WaitForMatch( + new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout)); + } + catch (EndOfStreamException ex) + { + throw new InvalidOperationException( + $"The NPM script '{npmScriptName}' exited without indicating that the " + + $"Angular CLI was listening for requests. The error output was: " + + $"{stdErrReader.ReadAsString()}", ex); + } + } + + var uri = new Uri(openBrowserLine.Groups[1].Value); + var serverInfo = new AngularCliServerInfo { Port = uri.Port }; + + // Even after the Angular CLI claims to be listening for requests, there's a short + // period where it will give an error if you make a request too quickly + await WaitForAngularCliServerToAcceptRequests(uri); + + return serverInfo; + } + + private static async Task WaitForAngularCliServerToAcceptRequests(Uri cliServerUri) + { + // To determine when it's actually ready, try making HEAD requests to '/'. If it + // produces any HTTP response (even if it's 404) then it's ready. If it rejects the + // connection then it's not ready. We keep trying forever because this is dev-mode + // only, and only a single startup attempt will be made, and there's a further level + // of timeouts enforced on a per-request basis. + var timeoutMilliseconds = 1000; + using (var client = new HttpClient()) + { + while (true) + { + try + { + // If we get any HTTP response, the CLI server is ready + await client.SendAsync( + new HttpRequestMessage(HttpMethod.Head, cliServerUri), + new CancellationTokenSource(timeoutMilliseconds).Token); + return; + } + catch (Exception) + { + await Task.Delay(500); + + // Depending on the host's networking configuration, the requests can take a while + // to go through, most likely due to the time spent resolving 'localhost'. + // Each time we have a failure, allow a bit longer next time (up to a maximum). + // This only influences the time until we regard the dev server as 'ready', so it + // doesn't affect the runtime perf (even in dev mode) once the first connection is made. + // Resolves https://github.com/aspnet/JavaScriptServices/issues/1611 + if (timeoutMilliseconds < 10000) + { + timeoutMilliseconds += 3000; + } + } + } + } + } + + class AngularCliServerInfo + { + public int Port { get; set; } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddlewareExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddlewareExtensions.cs new file mode 100644 index 0000000000..28e63c8e35 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddlewareExtensions.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using System; + +namespace Microsoft.AspNetCore.SpaServices.AngularCli +{ + /// + /// Extension methods for enabling Angular CLI middleware support. + /// + public static class AngularCliMiddlewareExtensions + { + /// + /// Handles requests by passing them through to an instance of the Angular CLI server. + /// This means you can always serve up-to-date CLI-built resources without having + /// to run the Angular CLI server manually. + /// + /// This feature should only be used in development. For production deployments, be + /// sure not to enable the Angular CLI server. + /// + /// The . + /// The name of the script in your package.json file that launches the Angular CLI process. + public static void UseAngularCliServer( + this ISpaBuilder spaBuilder, + string npmScript) + { + if (spaBuilder == null) + { + throw new ArgumentNullException(nameof(spaBuilder)); + } + + var spaOptions = spaBuilder.Options; + + if (string.IsNullOrEmpty(spaOptions.SourcePath)) + { + throw new InvalidOperationException($"To use {nameof(UseAngularCliServer)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}."); + } + + AngularCliMiddleware.Attach(spaBuilder, npmScript); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/DefaultSpaBuilder.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/DefaultSpaBuilder.cs new file mode 100644 index 0000000000..b1517a71e3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/DefaultSpaBuilder.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using System; + +namespace Microsoft.AspNetCore.SpaServices +{ + internal class DefaultSpaBuilder : ISpaBuilder + { + public IApplicationBuilder ApplicationBuilder { get; } + + public SpaOptions Options { get; } + + public DefaultSpaBuilder(IApplicationBuilder applicationBuilder, SpaOptions options) + { + ApplicationBuilder = applicationBuilder + ?? throw new ArgumentNullException(nameof(applicationBuilder)); + + Options = options + ?? throw new ArgumentNullException(nameof(options)); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ISpaBuilder.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ISpaBuilder.cs new file mode 100644 index 0000000000..491179268d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ISpaBuilder.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; + +namespace Microsoft.AspNetCore.SpaServices +{ + /// + /// Defines a class that provides mechanisms for configuring the hosting + /// of a Single Page Application (SPA) and attaching middleware. + /// + public interface ISpaBuilder + { + /// + /// The representing the middleware pipeline + /// in which the SPA is being hosted. + /// + IApplicationBuilder ApplicationBuilder { get; } + + /// + /// Describes configuration options for hosting a SPA. + /// + SpaOptions Options { get; } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Microsoft.AspNetCore.SpaServices.Extensions.csproj b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Microsoft.AspNetCore.SpaServices.Extensions.csproj new file mode 100644 index 0000000000..4472217f8f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Microsoft.AspNetCore.SpaServices.Extensions.csproj @@ -0,0 +1,18 @@ + + + + Helpers for building single-page applications on ASP.NET MVC Core. + netstandard2.0 + + + + + + + + + + + + + diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Npm/NpmScriptRunner.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Npm/NpmScriptRunner.cs new file mode 100644 index 0000000000..378ec5f9fa --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Npm/NpmScriptRunner.cs @@ -0,0 +1,131 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.NodeServices.Util; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Collections.Generic; + +// This is under the NodeServices namespace because post 2.1 it will be moved to that package +namespace Microsoft.AspNetCore.NodeServices.Npm +{ + /// + /// Executes the script entries defined in a package.json file, + /// capturing any output written to stdio. + /// + internal class NpmScriptRunner + { + public EventedStreamReader StdOut { get; } + public EventedStreamReader StdErr { get; } + + private static Regex AnsiColorRegex = new Regex("\x001b\\[[0-9;]*m", RegexOptions.None, TimeSpan.FromSeconds(1)); + + public NpmScriptRunner(string workingDirectory, string scriptName, string arguments, IDictionary envVars) + { + if (string.IsNullOrEmpty(workingDirectory)) + { + throw new ArgumentException("Cannot be null or empty.", nameof(workingDirectory)); + } + + if (string.IsNullOrEmpty(scriptName)) + { + throw new ArgumentException("Cannot be null or empty.", nameof(scriptName)); + } + + var npmExe = "npm"; + var completeArguments = $"run {scriptName} -- {arguments ?? string.Empty}"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // On Windows, the NPM executable is a .cmd file, so it can't be executed + // directly (except with UseShellExecute=true, but that's no good, because + // it prevents capturing stdio). So we need to invoke it via "cmd /c". + npmExe = "cmd"; + completeArguments = $"/c npm {completeArguments}"; + } + + var processStartInfo = new ProcessStartInfo(npmExe) + { + Arguments = completeArguments, + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + WorkingDirectory = workingDirectory + }; + + if (envVars != null) + { + foreach (var keyValuePair in envVars) + { + processStartInfo.Environment[keyValuePair.Key] = keyValuePair.Value; + } + } + + var process = LaunchNodeProcess(processStartInfo); + StdOut = new EventedStreamReader(process.StandardOutput); + StdErr = new EventedStreamReader(process.StandardError); + } + + public void AttachToLogger(ILogger logger) + { + // When the NPM task emits complete lines, pass them through to the real logger + StdOut.OnReceivedLine += line => + { + if (!string.IsNullOrWhiteSpace(line)) + { + // NPM tasks commonly emit ANSI colors, but it wouldn't make sense to forward + // those to loggers (because a logger isn't necessarily any kind of terminal) + logger.LogInformation(StripAnsiColors(line)); + } + }; + + StdErr.OnReceivedLine += line => + { + if (!string.IsNullOrWhiteSpace(line)) + { + logger.LogError(StripAnsiColors(line)); + } + }; + + // But when it emits incomplete lines, assume this is progress information and + // hence just pass it through to StdOut regardless of logger config. + StdErr.OnReceivedChunk += chunk => + { + var containsNewline = Array.IndexOf( + chunk.Array, '\n', chunk.Offset, chunk.Count) >= 0; + if (!containsNewline) + { + Console.Write(chunk.Array, chunk.Offset, chunk.Count); + } + }; + } + + private static string StripAnsiColors(string line) + => AnsiColorRegex.Replace(line, string.Empty); + + private static Process LaunchNodeProcess(ProcessStartInfo startInfo) + { + try + { + var process = Process.Start(startInfo); + + // See equivalent comment in OutOfProcessNodeInstance.cs for why + process.EnableRaisingEvents = true; + + return process; + } + catch (Exception ex) + { + var message = $"Failed to start 'npm'. To resolve this:.\n\n" + + "[1] Ensure that 'npm' is installed and can be found in one of the PATH directories.\n" + + $" Current PATH enviroment variable is: { Environment.GetEnvironmentVariable("PATH") }\n" + + " Make sure the executable is in one of those directories, or update your PATH.\n\n" + + "[2] See the InnerException for further details of the cause."; + throw new InvalidOperationException(message, ex); + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/ISpaPrerendererBuilder.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/ISpaPrerendererBuilder.cs new file mode 100644 index 0000000000..9c3171de87 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/ISpaPrerendererBuilder.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.SpaServices.Prerendering +{ + /// + /// Represents the ability to build a Single Page Application (SPA) on demand + /// so that it can be prerendered. This is only intended to be used at development + /// time. In production, a SPA should already have been built during publishing. + /// + public interface ISpaPrerendererBuilder + { + /// + /// Builds the Single Page Application so that a JavaScript entrypoint file + /// exists on disk. Prerendering middleware can then execute that file in + /// a Node environment. + /// + /// The . + /// A representing completion of the build process. + Task Build(ISpaBuilder spaBuilder); + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingExtensions.cs new file mode 100644 index 0000000000..b791b3a6f0 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingExtensions.cs @@ -0,0 +1,269 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.SpaServices; +using Microsoft.AspNetCore.SpaServices.Extensions.Util; +using Microsoft.AspNetCore.SpaServices.Prerendering; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Extension methods for configuring prerendering of a Single Page Application. + /// + public static class SpaPrerenderingExtensions + { + /// + /// Enables server-side prerendering middleware for a Single Page Application. + /// + /// The . + /// Supplies configuration for the prerendering middleware. + public static void UseSpaPrerendering( + this ISpaBuilder spaBuilder, + Action configuration) + { + if (spaBuilder == null) + { + throw new ArgumentNullException(nameof(spaBuilder)); + } + + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + var options = new SpaPrerenderingOptions(); + configuration.Invoke(options); + + var capturedBootModulePath = options.BootModulePath; + if (string.IsNullOrEmpty(capturedBootModulePath)) + { + throw new InvalidOperationException($"To use {nameof(UseSpaPrerendering)}, you " + + $"must set a nonempty value on the ${nameof(SpaPrerenderingOptions.BootModulePath)} " + + $"property on the ${nameof(SpaPrerenderingOptions)}."); + } + + // If we're building on demand, start that process in the background now + var buildOnDemandTask = options.BootModuleBuilder?.Build(spaBuilder); + + // Get all the necessary context info that will be used for each prerendering call + var applicationBuilder = spaBuilder.ApplicationBuilder; + var serviceProvider = applicationBuilder.ApplicationServices; + var nodeServices = GetNodeServices(serviceProvider); + var applicationStoppingToken = serviceProvider.GetRequiredService() + .ApplicationStopping; + var applicationBasePath = serviceProvider.GetRequiredService() + .ContentRootPath; + var moduleExport = new JavaScriptModuleExport(capturedBootModulePath); + var excludePathStrings = (options.ExcludeUrls ?? Array.Empty()) + .Select(url => new PathString(url)) + .ToArray(); + var buildTimeout = spaBuilder.Options.StartupTimeout; + + applicationBuilder.Use(async (context, next) => + { + // If this URL is excluded, skip prerendering. + // This is typically used to ensure that static client-side resources + // (e.g., /dist/*.css) are served normally or through SPA development + // middleware, and don't return the prerendered index.html page. + foreach (var excludePathString in excludePathStrings) + { + if (context.Request.Path.StartsWithSegments(excludePathString)) + { + await next(); + return; + } + } + + // If we're building on demand, wait for that to finish, or raise any build errors + if (buildOnDemandTask != null && !buildOnDemandTask.IsCompleted) + { + // For better debuggability, create a per-request timeout that makes it clear if the + // prerendering builder took too long for this request, but without aborting the + // underlying build task so that subsequent requests could still work. + await buildOnDemandTask.WithTimeout(buildTimeout, + $"The prerendering build process did not complete within the " + + $"timeout period of {buildTimeout.Seconds} seconds. " + + $"Check the log output for error information."); + } + + // It's no good if we try to return a 304. We need to capture the actual + // HTML content so it can be passed as a template to the prerenderer. + RemoveConditionalRequestHeaders(context.Request); + + // Make sure we're not capturing compressed content, because then we'd have + // to decompress it. Since this sub-request isn't leaving the machine, there's + // little to no benefit in having compression on it. + var originalAcceptEncodingValue = GetAndRemoveAcceptEncodingHeader(context.Request); + + // Capture the non-prerendered responses, which in production will typically only + // be returning the default SPA index.html page (because other resources will be + // served statically from disk). We will use this as a template in which to inject + // the prerendered output. + using (var outputBuffer = new MemoryStream()) + { + var originalResponseStream = context.Response.Body; + context.Response.Body = outputBuffer; + + try + { + await next(); + outputBuffer.Seek(0, SeekOrigin.Begin); + } + finally + { + context.Response.Body = originalResponseStream; + + if (!string.IsNullOrEmpty(originalAcceptEncodingValue)) + { + context.Request.Headers[HeaderNames.AcceptEncoding] = originalAcceptEncodingValue; + } + } + + // If it isn't an HTML page that we can use as the template for prerendering, + // - ... because it's not text/html + // - ... or because it's an error + // then prerendering doesn't apply to this request, so just pass through the + // response as-is. Note that the non-text/html case is not an error: this is + // typically how the SPA dev server responses for static content are returned + // in development mode. + var canPrerender = IsSuccessStatusCode(context.Response.StatusCode) + && IsHtmlContentType(context.Response.ContentType); + if (!canPrerender) + { + await outputBuffer.CopyToAsync(context.Response.Body); + return; + } + + // Most prerendering logic will want to know about the original, unprerendered + // HTML that the client would be getting otherwise. Typically this is used as + // a template from which the fully prerendered page can be generated. + var customData = new Dictionary + { + { "originalHtml", Encoding.UTF8.GetString(outputBuffer.GetBuffer()) } + }; + + // If the developer wants to use custom logic to pass arbitrary data to the + // prerendering JS code (e.g., to pass through cookie data), now's their chance + options.SupplyData?.Invoke(context, customData); + + var (unencodedAbsoluteUrl, unencodedPathAndQuery) + = GetUnencodedUrlAndPathQuery(context); + var renderResult = await Prerenderer.RenderToString( + applicationBasePath, + nodeServices, + applicationStoppingToken, + moduleExport, + unencodedAbsoluteUrl, + unencodedPathAndQuery, + customDataParameter: customData, + timeoutMilliseconds: 0, + requestPathBase: context.Request.PathBase.ToString()); + + await ServePrerenderResult(context, renderResult); + } + }); + } + + private static bool IsHtmlContentType(string contentType) + { + if (string.Equals(contentType, "text/html", StringComparison.Ordinal)) + { + return true; + } + + return contentType != null + && contentType.StartsWith("text/html;", StringComparison.Ordinal); + } + + private static bool IsSuccessStatusCode(int statusCode) + => statusCode >= 200 && statusCode < 300; + + private static void RemoveConditionalRequestHeaders(HttpRequest request) + { + request.Headers.Remove(HeaderNames.IfMatch); + request.Headers.Remove(HeaderNames.IfModifiedSince); + request.Headers.Remove(HeaderNames.IfNoneMatch); + request.Headers.Remove(HeaderNames.IfUnmodifiedSince); + request.Headers.Remove(HeaderNames.IfRange); + } + + private static string GetAndRemoveAcceptEncodingHeader(HttpRequest request) + { + var headers = request.Headers; + var value = (string)null; + + if (headers.ContainsKey(HeaderNames.AcceptEncoding)) + { + value = headers[HeaderNames.AcceptEncoding]; + headers.Remove(HeaderNames.AcceptEncoding); + } + + return value; + } + + private static (string, string) GetUnencodedUrlAndPathQuery(HttpContext httpContext) + { + // This is a duplicate of code from Prerenderer.cs in the SpaServices package. + // Once the SpaServices.Extension package implementation gets merged back into + // SpaServices, this duplicate can be removed. To remove this, change the code + // above that calls Prerenderer.RenderToString to use the internal overload + // that takes an HttpContext instead of a url/path+query pair. + var requestFeature = httpContext.Features.Get(); + var unencodedPathAndQuery = requestFeature.RawTarget; + var request = httpContext.Request; + var unencodedAbsoluteUrl = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}"; + return (unencodedAbsoluteUrl, unencodedPathAndQuery); + } + + private static async Task ServePrerenderResult(HttpContext context, RenderToStringResult renderResult) + { + context.Response.Clear(); + + if (!string.IsNullOrEmpty(renderResult.RedirectUrl)) + { + var permanentRedirect = renderResult.StatusCode.GetValueOrDefault() == 301; + context.Response.Redirect(renderResult.RedirectUrl, permanentRedirect); + } + else + { + // The Globals property exists for back-compatibility but is meaningless + // for prerendering that returns complete HTML pages + if (renderResult.Globals != null) + { + throw new InvalidOperationException($"{nameof(renderResult.Globals)} is not " + + $"supported when prerendering via {nameof(UseSpaPrerendering)}(). Instead, " + + $"your prerendering logic should return a complete HTML page, in which you " + + $"embed any information you wish to return to the client."); + } + + if (renderResult.StatusCode.HasValue) + { + context.Response.StatusCode = renderResult.StatusCode.Value; + } + + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync(renderResult.Html); + } + } + + private static INodeServices GetNodeServices(IServiceProvider serviceProvider) + { + // Use the registered instance, or create a new private instance if none is registered + var instance = (INodeServices)serviceProvider.GetService(typeof(INodeServices)); + return instance ?? NodeServicesFactory.CreateNodeServices( + new NodeServicesOptions(serviceProvider)); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingOptions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingOptions.cs new file mode 100644 index 0000000000..bc121bbc9a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingOptions.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SpaServices.Prerendering; +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Represents options for the SPA prerendering middleware. + /// + public class SpaPrerenderingOptions + { + /// + /// Gets or sets an that the prerenderer will invoke before + /// looking for the boot module file. + /// + /// This is only intended to be used during development as a way of generating the JavaScript boot + /// file automatically when the application runs. This property should be left as null in + /// production applications. + /// + public ISpaPrerendererBuilder BootModuleBuilder { get; set; } + + /// + /// Gets or sets the path, relative to your application root, of the JavaScript file + /// containing prerendering logic. + /// + public string BootModulePath { get; set; } + + /// + /// Gets or sets an array of URL prefixes for which prerendering should not run. + /// + public string[] ExcludeUrls { get; set; } + + /// + /// Gets or sets a callback that will be invoked during prerendering, allowing you to pass additional + /// data to the prerendering entrypoint code. + /// + public Action> SupplyData { get; set; } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/ConditionalProxyMiddleware.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/ConditionalProxyMiddleware.cs new file mode 100644 index 0000000000..96f62ce756 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/ConditionalProxyMiddleware.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Hosting; +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.SpaServices.Extensions.Proxy +{ + // This duplicates and updates the proxying logic in SpaServices so that we can update + // the project templates without waiting for 2.1 to ship. When 2.1 is ready to ship, + // merge the additional proxying features (e.g., proxying websocket connections) back + // into the SpaServices proxying code. It's all internal. + internal class ConditionalProxyMiddleware + { + private readonly RequestDelegate _next; + private readonly Task _baseUriTask; + private readonly string _pathPrefix; + private readonly bool _pathPrefixIsRoot; + private readonly HttpClient _httpClient; + private readonly CancellationToken _applicationStoppingToken; + + public ConditionalProxyMiddleware( + RequestDelegate next, + string pathPrefix, + TimeSpan requestTimeout, + Task baseUriTask, + IApplicationLifetime applicationLifetime) + { + if (!pathPrefix.StartsWith("/")) + { + pathPrefix = "/" + pathPrefix; + } + + _next = next; + _pathPrefix = pathPrefix; + _pathPrefixIsRoot = string.Equals(_pathPrefix, "/", StringComparison.Ordinal); + _baseUriTask = baseUriTask; + _httpClient = SpaProxy.CreateHttpClientForProxy(requestTimeout); + _applicationStoppingToken = applicationLifetime.ApplicationStopping; + } + + public async Task Invoke(HttpContext context) + { + if (context.Request.Path.StartsWithSegments(_pathPrefix) || _pathPrefixIsRoot) + { + var didProxyRequest = await SpaProxy.PerformProxyRequest( + context, _httpClient, _baseUriTask, _applicationStoppingToken, proxy404s: false); + if (didProxyRequest) + { + return; + } + } + + // Not a request we can proxy + await _next.Invoke(context); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/SpaProxy.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/SpaProxy.cs new file mode 100644 index 0000000000..bda454a4c9 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/SpaProxy.cs @@ -0,0 +1,302 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.SpaServices.Extensions.Proxy +{ + // This duplicates and updates the proxying logic in SpaServices so that we can update + // the project templates without waiting for 2.1 to ship. When 2.1 is ready to ship, + // remove the old ConditionalProxy.cs from SpaServices and replace its usages with this. + // Doesn't affect public API surface - it's all internal. + internal static class SpaProxy + { + private const int DefaultWebSocketBufferSize = 4096; + private const int StreamCopyBufferSize = 81920; + + // Don't forward User-Agent/Accept because of https://github.com/aspnet/JavaScriptServices/issues/1469 + // Others just aren't applicable in proxy scenarios + private static readonly string[] NotForwardedWebSocketHeaders = new[] { "Accept", "Connection", "Host", "User-Agent", "Upgrade", "Sec-WebSocket-Key", "Sec-WebSocket-Version" }; + + public static HttpClient CreateHttpClientForProxy(TimeSpan requestTimeout) + { + var handler = new HttpClientHandler + { + AllowAutoRedirect = false, + UseCookies = false, + + }; + + return new HttpClient(handler) + { + Timeout = requestTimeout + }; + } + + public static async Task PerformProxyRequest( + HttpContext context, + HttpClient httpClient, + Task baseUriTask, + CancellationToken applicationStoppingToken, + bool proxy404s) + { + // Stop proxying if either the server or client wants to disconnect + var proxyCancellationToken = CancellationTokenSource.CreateLinkedTokenSource( + context.RequestAborted, + applicationStoppingToken).Token; + + // We allow for the case where the target isn't known ahead of time, and want to + // delay proxied requests until the target becomes known. This is useful, for example, + // when proxying to Angular CLI middleware: we won't know what port it's listening + // on until it finishes starting up. + var baseUri = await baseUriTask; + var targetUri = new Uri( + baseUri, + context.Request.Path + context.Request.QueryString); + + try + { + if (context.WebSockets.IsWebSocketRequest) + { + await AcceptProxyWebSocketRequest(context, ToWebSocketScheme(targetUri), proxyCancellationToken); + return true; + } + else + { + using (var requestMessage = CreateProxyHttpRequest(context, targetUri)) + using (var responseMessage = await httpClient.SendAsync( + requestMessage, + HttpCompletionOption.ResponseHeadersRead, + proxyCancellationToken)) + { + if (!proxy404s) + { + if (responseMessage.StatusCode == HttpStatusCode.NotFound) + { + // We're not proxying 404s, i.e., we want to resume the middleware pipeline + // and let some other middleware handle this. + return false; + } + } + + await CopyProxyHttpResponse(context, responseMessage, proxyCancellationToken); + return true; + } + } + } + catch (OperationCanceledException) + { + // If we're aborting because either the client disconnected, or the server + // is shutting down, don't treat this as an error. + return true; + } + catch (IOException) + { + // This kind of exception can also occur if a proxy read/write gets interrupted + // due to the process shutting down. + return true; + } + catch (HttpRequestException ex) + { + throw new HttpRequestException( + $"Failed to proxy the request to {targetUri.ToString()}, because the request to " + + $"the proxy target failed. Check that the proxy target server is running and " + + $"accepting requests to {baseUri.ToString()}.\n\n" + + $"The underlying exception message was '{ex.Message}'." + + $"Check the InnerException for more details.", ex); + } + } + + private static HttpRequestMessage CreateProxyHttpRequest(HttpContext context, Uri uri) + { + var request = context.Request; + + var requestMessage = new HttpRequestMessage(); + var requestMethod = request.Method; + if (!HttpMethods.IsGet(requestMethod) && + !HttpMethods.IsHead(requestMethod) && + !HttpMethods.IsDelete(requestMethod) && + !HttpMethods.IsTrace(requestMethod)) + { + var streamContent = new StreamContent(request.Body); + requestMessage.Content = streamContent; + } + + // Copy the request headers + foreach (var header in request.Headers) + { + if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null) + { + requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + } + + requestMessage.Headers.Host = uri.Authority; + requestMessage.RequestUri = uri; + requestMessage.Method = new HttpMethod(request.Method); + + return requestMessage; + } + + private static async Task CopyProxyHttpResponse(HttpContext context, HttpResponseMessage responseMessage, CancellationToken cancellationToken) + { + context.Response.StatusCode = (int)responseMessage.StatusCode; + foreach (var header in responseMessage.Headers) + { + context.Response.Headers[header.Key] = header.Value.ToArray(); + } + + foreach (var header in responseMessage.Content.Headers) + { + context.Response.Headers[header.Key] = header.Value.ToArray(); + } + + // SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response. + context.Response.Headers.Remove("transfer-encoding"); + + using (var responseStream = await responseMessage.Content.ReadAsStreamAsync()) + { + await responseStream.CopyToAsync(context.Response.Body, StreamCopyBufferSize, cancellationToken); + } + } + + private static Uri ToWebSocketScheme(Uri uri) + { + if (uri == null) + { + throw new ArgumentNullException(nameof(uri)); + } + + var uriBuilder = new UriBuilder(uri); + if (string.Equals(uriBuilder.Scheme, "https", StringComparison.OrdinalIgnoreCase)) + { + uriBuilder.Scheme = "wss"; + } + else if (string.Equals(uriBuilder.Scheme, "http", StringComparison.OrdinalIgnoreCase)) + { + uriBuilder.Scheme = "ws"; + } + + return uriBuilder.Uri; + } + + private static async Task AcceptProxyWebSocketRequest(HttpContext context, Uri destinationUri, CancellationToken cancellationToken) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (destinationUri == null) + { + throw new ArgumentNullException(nameof(destinationUri)); + } + + using (var client = new ClientWebSocket()) + { + foreach (var headerEntry in context.Request.Headers) + { + if (!NotForwardedWebSocketHeaders.Contains(headerEntry.Key, StringComparer.OrdinalIgnoreCase)) + { + try + { + client.Options.SetRequestHeader(headerEntry.Key, headerEntry.Value); + } + catch (ArgumentException) + { + // On net461, certain header names are reserved and can't be set. + // We filter out the known ones via the test above, but there could + // be others arbitrarily set by the client. It's not helpful to + // consider it an error, so just skip non-forwardable headers. + // The perf implications of handling this via a catch aren't an + // issue since this is a dev-time only feature. + } + } + } + + try + { + // Note that this is not really good enough to make Websockets work with + // Angular CLI middleware. For some reason, ConnectAsync takes over 1 second, + // on Windows, by which time the logic in SockJS has already timed out and made + // it fall back on some other transport (xhr_streaming, usually). It's fine + // on Linux though, completing almost instantly. + // + // The slowness on Windows does not cause a problem though, because the transport + // fallback logic works correctly and doesn't surface any errors, but it would be + // better if ConnectAsync was fast enough and the initial Websocket transport + // could actually be used. + await client.ConnectAsync(destinationUri, cancellationToken); + } + catch (WebSocketException) + { + context.Response.StatusCode = 400; + return false; + } + + using (var server = await context.WebSockets.AcceptWebSocketAsync(client.SubProtocol)) + { + var bufferSize = DefaultWebSocketBufferSize; + await Task.WhenAll( + PumpWebSocket(client, server, bufferSize, cancellationToken), + PumpWebSocket(server, client, bufferSize, cancellationToken)); + } + + return true; + } + } + + private static async Task PumpWebSocket(WebSocket source, WebSocket destination, int bufferSize, CancellationToken cancellationToken) + { + if (bufferSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bufferSize)); + } + + var buffer = new byte[bufferSize]; + + while (true) + { + // Because WebSocket.ReceiveAsync doesn't work well with CancellationToken (it doesn't + // actually exit when the token notifies, at least not in the 'server' case), use + // polling. The perf might not be ideal, but this is a dev-time feature only. + var resultTask = source.ReceiveAsync(new ArraySegment(buffer), cancellationToken); + while (true) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + if (resultTask.IsCompleted) + { + break; + } + + await Task.Delay(100); + } + + var result = resultTask.Result; // We know it's completed already + if (result.MessageType == WebSocketMessageType.Close) + { + if (destination.State == WebSocketState.Open || destination.State == WebSocketState.CloseReceived) + { + await destination.CloseOutputAsync(source.CloseStatus.Value, source.CloseStatusDescription, cancellationToken); + } + + return; + } + + await destination.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, cancellationToken); + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/SpaProxyingExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/SpaProxyingExtensions.cs new file mode 100644 index 0000000000..3270c1b8aa --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/SpaProxyingExtensions.cs @@ -0,0 +1,92 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.SpaServices; +using Microsoft.AspNetCore.SpaServices.Extensions.Proxy; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Extension methods for proxying requests to a local SPA development server during + /// development. Not for use in production applications. + /// + public static class SpaProxyingExtensions + { + /// + /// Configures the application to forward incoming requests to a local Single Page + /// Application (SPA) development server. This is only intended to be used during + /// development. Do not enable this middleware in production applications. + /// + /// The . + /// The target base URI to which requests should be proxied. + public static void UseProxyToSpaDevelopmentServer( + this ISpaBuilder spaBuilder, + string baseUri) + { + UseProxyToSpaDevelopmentServer( + spaBuilder, + new Uri(baseUri)); + } + + /// + /// Configures the application to forward incoming requests to a local Single Page + /// Application (SPA) development server. This is only intended to be used during + /// development. Do not enable this middleware in production applications. + /// + /// The . + /// The target base URI to which requests should be proxied. + public static void UseProxyToSpaDevelopmentServer( + this ISpaBuilder spaBuilder, + Uri baseUri) + { + UseProxyToSpaDevelopmentServer( + spaBuilder, + () => Task.FromResult(baseUri)); + } + + /// + /// Configures the application to forward incoming requests to a local Single Page + /// Application (SPA) development server. This is only intended to be used during + /// development. Do not enable this middleware in production applications. + /// + /// The . + /// A callback that will be invoked on each request to supply a that resolves with the target base URI to which requests should be proxied. + public static void UseProxyToSpaDevelopmentServer( + this ISpaBuilder spaBuilder, + Func> baseUriTaskFactory) + { + var applicationBuilder = spaBuilder.ApplicationBuilder; + var applicationStoppingToken = GetStoppingToken(applicationBuilder); + + // Since we might want to proxy WebSockets requests (e.g., by default, AngularCliMiddleware + // requires it), enable it for the app + applicationBuilder.UseWebSockets(); + + // It's important not to time out the requests, as some of them might be to + // server-sent event endpoints or similar, where it's expected that the response + // takes an unlimited time and never actually completes + var neverTimeOutHttpClient = + SpaProxy.CreateHttpClientForProxy(Timeout.InfiniteTimeSpan); + + // Proxy all requests to the SPA development server + applicationBuilder.Use(async (context, next) => + { + var didProxyRequest = await SpaProxy.PerformProxyRequest( + context, neverTimeOutHttpClient, baseUriTaskFactory(), applicationStoppingToken, + proxy404s: true); + }); + } + + private static CancellationToken GetStoppingToken(IApplicationBuilder appBuilder) + { + var applicationLifetime = appBuilder + .ApplicationServices + .GetService(typeof(IApplicationLifetime)); + return ((IApplicationLifetime)applicationLifetime).ApplicationStopping; + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ReactDevelopmentServer/ReactDevelopmentServerMiddleware.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ReactDevelopmentServer/ReactDevelopmentServerMiddleware.cs new file mode 100644 index 0000000000..78a7b4f03f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ReactDevelopmentServer/ReactDevelopmentServerMiddleware.cs @@ -0,0 +1,101 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.NodeServices.Npm; +using Microsoft.AspNetCore.NodeServices.Util; +using Microsoft.AspNetCore.SpaServices.Util; +using System; +using System.IO; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SpaServices.Extensions.Util; + +namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer +{ + internal static class ReactDevelopmentServerMiddleware + { + private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices"; + private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine + + public static void Attach( + ISpaBuilder spaBuilder, + string npmScriptName) + { + var sourcePath = spaBuilder.Options.SourcePath; + if (string.IsNullOrEmpty(sourcePath)) + { + throw new ArgumentException("Cannot be null or empty", nameof(sourcePath)); + } + + if (string.IsNullOrEmpty(npmScriptName)) + { + throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName)); + } + + // Start create-react-app and attach to middleware pipeline + var appBuilder = spaBuilder.ApplicationBuilder; + var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName); + var portTask = StartCreateReactAppServerAsync(sourcePath, npmScriptName, logger); + + // Everything we proxy is hardcoded to target http://localhost because: + // - the requests are always from the local machine (we're not accepting remote + // requests that go directly to the create-react-app server) + // - given that, there's no reason to use https, and we couldn't even if we + // wanted to, because in general the create-react-app server has no certificate + var targetUriTask = portTask.ContinueWith( + task => new UriBuilder("http", "localhost", task.Result).Uri); + + SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () => + { + // On each request, we create a separate startup task with its own timeout. That way, even if + // the first request times out, subsequent requests could still work. + var timeout = spaBuilder.Options.StartupTimeout; + return targetUriTask.WithTimeout(timeout, + $"The create-react-app server did not start listening for requests " + + $"within the timeout period of {timeout.Seconds} seconds. " + + $"Check the log output for error information."); + }); + } + + private static async Task StartCreateReactAppServerAsync( + string sourcePath, string npmScriptName, ILogger logger) + { + var portNumber = TcpPortFinder.FindAvailablePort(); + logger.LogInformation($"Starting create-react-app server on port {portNumber}..."); + + var envVars = new Dictionary + { + { "PORT", portNumber.ToString() }, + { "BROWSER", "none" }, // We don't want create-react-app to open its own extra browser window pointing to the internal dev server port + }; + var npmScriptRunner = new NpmScriptRunner( + sourcePath, npmScriptName, null, envVars); + npmScriptRunner.AttachToLogger(logger); + + using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr)) + { + try + { + // Although the React dev server may eventually tell us the URL it's listening on, + // it doesn't do so until it's finished compiling, and even then only if there were + // no compiler warnings. So instead of waiting for that, consider it ready as soon + // as it starts listening for requests. + await npmScriptRunner.StdOut.WaitForMatch( + new Regex("Starting the development server", RegexOptions.None, RegexMatchTimeout)); + } + catch (EndOfStreamException ex) + { + throw new InvalidOperationException( + $"The NPM script '{npmScriptName}' exited without indicating that the " + + $"create-react-app server was listening for requests. The error output was: " + + $"{stdErrReader.ReadAsString()}", ex); + } + } + + return portNumber; + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ReactDevelopmentServer/ReactDevelopmentServerMiddlewareExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ReactDevelopmentServer/ReactDevelopmentServerMiddlewareExtensions.cs new file mode 100644 index 0000000000..f58a6d1a9d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/ReactDevelopmentServer/ReactDevelopmentServerMiddlewareExtensions.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using System; + +namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer +{ + /// + /// Extension methods for enabling React development server middleware support. + /// + public static class ReactDevelopmentServerMiddlewareExtensions + { + /// + /// Handles requests by passing them through to an instance of the create-react-app server. + /// This means you can always serve up-to-date CLI-built resources without having + /// to run the create-react-app server manually. + /// + /// This feature should only be used in development. For production deployments, be + /// sure not to enable the create-react-app server. + /// + /// The . + /// The name of the script in your package.json file that launches the create-react-app server. + public static void UseReactDevelopmentServer( + this ISpaBuilder spaBuilder, + string npmScript) + { + if (spaBuilder == null) + { + throw new ArgumentNullException(nameof(spaBuilder)); + } + + var spaOptions = spaBuilder.Options; + + if (string.IsNullOrEmpty(spaOptions.SourcePath)) + { + throw new InvalidOperationException($"To use {nameof(UseReactDevelopmentServer)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}."); + } + + ReactDevelopmentServerMiddleware.Attach(spaBuilder, npmScript); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaApplicationBuilderExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..f127e355c3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaApplicationBuilderExtensions.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.SpaServices; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Provides extension methods used for configuring an application to + /// host a client-side Single Page Application (SPA). + /// + public static class SpaApplicationBuilderExtensions + { + /// + /// Handles all requests from this point in the middleware chain by returning + /// the default page for the Single Page Application (SPA). + /// + /// This middleware should be placed late in the chain, so that other middleware + /// for serving static files, MVC actions, etc., takes precedence. + /// + /// The . + /// + /// This callback will be invoked so that additional middleware can be registered within + /// the context of this SPA. + /// + public static void UseSpa(this IApplicationBuilder app, Action configuration) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + // Use the options configured in DI (or blank if none was configured). We have to clone it + // otherwise if you have multiple UseSpa calls, their configurations would interfere with one another. + var optionsProvider = app.ApplicationServices.GetService>(); + var options = new SpaOptions(optionsProvider.Value); + + var spaBuilder = new DefaultSpaBuilder(app, options); + configuration.Invoke(spaBuilder); + SpaDefaultPageMiddleware.Attach(spaBuilder); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaDefaultPageMiddleware.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaDefaultPageMiddleware.cs new file mode 100644 index 0000000000..c3c540b150 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaDefaultPageMiddleware.cs @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace Microsoft.AspNetCore.SpaServices +{ + internal class SpaDefaultPageMiddleware + { + public static void Attach(ISpaBuilder spaBuilder) + { + if (spaBuilder == null) + { + throw new ArgumentNullException(nameof(spaBuilder)); + } + + var app = spaBuilder.ApplicationBuilder; + var options = spaBuilder.Options; + + // Rewrite all requests to the default page + app.Use((context, next) => + { + context.Request.Path = options.DefaultPage; + return next(); + }); + + // Serve it as a static file + // Developers who need to host more than one SPA with distinct default pages can + // override the file provider + app.UseSpaStaticFilesInternal( + options.DefaultPageStaticFileOptions ?? new StaticFileOptions(), + allowFallbackOnServingWebRootFiles: true); + + // If the default file didn't get served as a static file (usually because it was not + // present on disk), the SPA is definitely not going to work. + app.Use((context, next) => + { + var message = "The SPA default page middleware could not return the default page " + + $"'{options.DefaultPage}' because it was not found, and no other middleware " + + "handled the request.\n"; + + // Try to clarify the common scenario where someone runs an application in + // Production environment without first publishing the whole application + // or at least building the SPA. + var hostEnvironment = (IHostingEnvironment)context.RequestServices.GetService(typeof(IHostingEnvironment)); + if (hostEnvironment != null && hostEnvironment.IsProduction()) + { + message += "Your application is running in Production mode, so make sure it has " + + "been published, or that you have built your SPA manually. Alternatively you " + + "may wish to switch to the Development environment.\n"; + } + + throw new InvalidOperationException(message); + }); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaOptions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaOptions.cs new file mode 100644 index 0000000000..f44b820f3e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/SpaOptions.cs @@ -0,0 +1,78 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.FileProviders; +using System; + +namespace Microsoft.AspNetCore.SpaServices +{ + /// + /// Describes options for hosting a Single Page Application (SPA). + /// + public class SpaOptions + { + private PathString _defaultPage = "/index.html"; + + /// + /// Constructs a new instance of . + /// + public SpaOptions() + { + } + + /// + /// Constructs a new instance of . + /// + /// An instance of from which values should be copied. + internal SpaOptions(SpaOptions copyFromOptions) + { + _defaultPage = copyFromOptions.DefaultPage; + DefaultPageStaticFileOptions = copyFromOptions.DefaultPageStaticFileOptions; + SourcePath = copyFromOptions.SourcePath; + } + + /// + /// Gets or sets the URL of the default page that hosts your SPA user interface. + /// The default value is "/index.html". + /// + public PathString DefaultPage + { + get => _defaultPage; + set + { + if (string.IsNullOrEmpty(value.Value)) + { + throw new ArgumentException($"The value for {nameof(DefaultPage)} cannot be null or empty."); + } + + _defaultPage = value; + } + } + + /// + /// Gets or sets the that supplies content + /// for serving the SPA's default page. + /// + /// If not set, a default file provider will read files from the + /// , which by default is + /// the wwwroot directory. + /// + public StaticFileOptions DefaultPageStaticFileOptions { get; set; } + + /// + /// Gets or sets the path, relative to the application working directory, + /// of the directory that contains the SPA source files during + /// development. The directory may not exist in published applications. + /// + public string SourcePath { get; set; } + + /// + /// Gets or sets the maximum duration that a request will wait for the SPA + /// to become ready to serve to the client. + /// + public TimeSpan StartupTimeout { get; set; } = TimeSpan.FromSeconds(50); + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/DefaultSpaStaticFileProvider.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/DefaultSpaStaticFileProvider.cs new file mode 100644 index 0000000000..0c2348feaa --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/DefaultSpaStaticFileProvider.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using System; +using System.IO; + +namespace Microsoft.AspNetCore.SpaServices.StaticFiles +{ + /// + /// Provides an implementation of that supplies + /// physical files at a location configured using . + /// + internal class DefaultSpaStaticFileProvider : ISpaStaticFileProvider + { + private IFileProvider _fileProvider; + + public DefaultSpaStaticFileProvider( + IServiceProvider serviceProvider, + SpaStaticFilesOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (string.IsNullOrEmpty(options.RootPath)) + { + throw new ArgumentException($"The {nameof(options.RootPath)} property " + + $"of {nameof(options)} cannot be null or empty."); + } + + var env = serviceProvider.GetRequiredService(); + var absoluteRootPath = Path.Combine( + env.ContentRootPath, + options.RootPath); + + // PhysicalFileProvider will throw if you pass a non-existent path, + // but we don't want that scenario to be an error because for SPA + // scenarios, it's better if non-existing directory just means we + // don't serve any static files. + if (Directory.Exists(absoluteRootPath)) + { + _fileProvider = new PhysicalFileProvider(absoluteRootPath); + } + } + + public IFileProvider FileProvider => _fileProvider; + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/ISpaStaticFileProvider.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/ISpaStaticFileProvider.cs new file mode 100644 index 0000000000..c0312d91c2 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/ISpaStaticFileProvider.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.FileProviders; + +namespace Microsoft.AspNetCore.SpaServices.StaticFiles +{ + /// + /// Represents a service that can provide static files to be served for a Single Page + /// Application (SPA). + /// + public interface ISpaStaticFileProvider + { + /// + /// Gets the file provider, if available, that supplies the static files for the SPA. + /// The value is null if no file provider is available. + /// + IFileProvider FileProvider { get; } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesExtensions.cs new file mode 100644 index 0000000000..7036aca32d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesExtensions.cs @@ -0,0 +1,147 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.SpaServices.StaticFiles; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; +using System; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for configuring an application to serve static files for a + /// Single Page Application (SPA). + /// + public static class SpaStaticFilesExtensions + { + /// + /// Registers an service that can provide static + /// files to be served for a Single Page Application (SPA). + /// + /// The . + /// If specified, this callback will be invoked to set additional configuration options. + public static void AddSpaStaticFiles( + this IServiceCollection services, + Action configuration = null) + { + services.AddSingleton(serviceProvider => + { + // Use the options configured in DI (or blank if none was configured) + var optionsProvider = serviceProvider.GetService>(); + var options = optionsProvider.Value; + + // Allow the developer to perform further configuration + configuration?.Invoke(options); + + if (string.IsNullOrEmpty(options.RootPath)) + { + throw new InvalidOperationException($"No {nameof(SpaStaticFilesOptions.RootPath)} " + + $"was set on the {nameof(SpaStaticFilesOptions)}."); + } + + return new DefaultSpaStaticFileProvider(serviceProvider, options); + }); + } + + /// + /// Configures the application to serve static files for a Single Page Application (SPA). + /// The files will be located using the registered service. + /// + /// The . + public static void UseSpaStaticFiles(this IApplicationBuilder applicationBuilder) + { + UseSpaStaticFiles(applicationBuilder, new StaticFileOptions()); + } + + /// + /// Configures the application to serve static files for a Single Page Application (SPA). + /// The files will be located using the registered service. + /// + /// The . + /// Specifies options for serving the static files. + public static void UseSpaStaticFiles(this IApplicationBuilder applicationBuilder, StaticFileOptions options) + { + if (applicationBuilder == null) + { + throw new ArgumentNullException(nameof(applicationBuilder)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + UseSpaStaticFilesInternal(applicationBuilder, + staticFileOptions: options, + allowFallbackOnServingWebRootFiles: false); + } + + internal static void UseSpaStaticFilesInternal( + this IApplicationBuilder app, + StaticFileOptions staticFileOptions, + bool allowFallbackOnServingWebRootFiles) + { + if (staticFileOptions == null) + { + throw new ArgumentNullException(nameof(staticFileOptions)); + } + + // If the file provider was explicitly supplied, that takes precedence over any other + // configured file provider. This is most useful if the application hosts multiple SPAs + // (via multiple calls to UseSpa()), so each needs to serve its own separate static files + // instead of using AddSpaStaticFiles/UseSpaStaticFiles. + // But if no file provider was specified, try to get one from the DI config. + if (staticFileOptions.FileProvider == null) + { + var shouldServeStaticFiles = ShouldServeStaticFiles( + app, + allowFallbackOnServingWebRootFiles, + out var fileProviderOrDefault); + if (shouldServeStaticFiles) + { + staticFileOptions.FileProvider = fileProviderOrDefault; + } + else + { + // The registered ISpaStaticFileProvider says we shouldn't + // serve static files + return; + } + } + + + app.UseStaticFiles(staticFileOptions); + } + + private static bool ShouldServeStaticFiles( + IApplicationBuilder app, + bool allowFallbackOnServingWebRootFiles, + out IFileProvider fileProviderOrDefault) + { + var spaStaticFilesService = app.ApplicationServices.GetService(); + if (spaStaticFilesService != null) + { + // If an ISpaStaticFileProvider was configured but it says no IFileProvider is available + // (i.e., it supplies 'null'), this implies we should not serve any static files. This + // is typically the case in development when SPA static files are being served from a + // SPA development server (e.g., Angular CLI or create-react-app), in which case no + // directory of prebuilt files will exist on disk. + fileProviderOrDefault = spaStaticFilesService.FileProvider; + return fileProviderOrDefault != null; + } + else if (!allowFallbackOnServingWebRootFiles) + { + throw new InvalidOperationException($"To use {nameof(UseSpaStaticFiles)}, you must " + + $"first register an {nameof(ISpaStaticFileProvider)} in the service provider, typically " + + $"by calling services.{nameof(AddSpaStaticFiles)}."); + } + else + { + // Fall back on serving wwwroot + fileProviderOrDefault = null; + return true; + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesOptions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesOptions.cs new file mode 100644 index 0000000000..82bde12b80 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesOptions.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.SpaServices.StaticFiles +{ + /// + /// Represents options for serving static files for a Single Page Application (SPA). + /// + public class SpaStaticFilesOptions + { + /// + /// Gets or sets the path, relative to the application root, of the directory in which + /// the physical files are located. + /// + /// If the specified directory does not exist, then the + /// + /// middleware will not serve any static files. + /// + public string RootPath { get; set; } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/EventedStreamReader.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/EventedStreamReader.cs new file mode 100644 index 0000000000..95e018a590 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/EventedStreamReader.cs @@ -0,0 +1,125 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.NodeServices.Util +{ + /// + /// Wraps a to expose an evented API, issuing notifications + /// when the stream emits partial lines, completed lines, or finally closes. + /// + internal class EventedStreamReader + { + public delegate void OnReceivedChunkHandler(ArraySegment chunk); + public delegate void OnReceivedLineHandler(string line); + public delegate void OnStreamClosedHandler(); + + public event OnReceivedChunkHandler OnReceivedChunk; + public event OnReceivedLineHandler OnReceivedLine; + public event OnStreamClosedHandler OnStreamClosed; + + private readonly StreamReader _streamReader; + private readonly StringBuilder _linesBuffer; + + public EventedStreamReader(StreamReader streamReader) + { + _streamReader = streamReader ?? throw new ArgumentNullException(nameof(streamReader)); + _linesBuffer = new StringBuilder(); + Task.Factory.StartNew(Run); + } + + public Task WaitForMatch(Regex regex) + { + var tcs = new TaskCompletionSource(); + var completionLock = new object(); + + OnReceivedLineHandler onReceivedLineHandler = null; + OnStreamClosedHandler onStreamClosedHandler = null; + + void ResolveIfStillPending(Action applyResolution) + { + lock (completionLock) + { + if (!tcs.Task.IsCompleted) + { + OnReceivedLine -= onReceivedLineHandler; + OnStreamClosed -= onStreamClosedHandler; + applyResolution(); + } + } + } + + onReceivedLineHandler = line => + { + var match = regex.Match(line); + if (match.Success) + { + ResolveIfStillPending(() => tcs.SetResult(match)); + } + }; + + onStreamClosedHandler = () => + { + ResolveIfStillPending(() => tcs.SetException(new EndOfStreamException())); + }; + + OnReceivedLine += onReceivedLineHandler; + OnStreamClosed += onStreamClosedHandler; + + return tcs.Task; + } + + private async Task Run() + { + var buf = new char[8 * 1024]; + while (true) + { + var chunkLength = await _streamReader.ReadAsync(buf, 0, buf.Length); + if (chunkLength == 0) + { + OnClosed(); + break; + } + + OnChunk(new ArraySegment(buf, 0, chunkLength)); + + var lineBreakPos = Array.IndexOf(buf, '\n', 0, chunkLength); + if (lineBreakPos < 0) + { + _linesBuffer.Append(buf, 0, chunkLength); + } + else + { + _linesBuffer.Append(buf, 0, lineBreakPos + 1); + OnCompleteLine(_linesBuffer.ToString()); + _linesBuffer.Clear(); + _linesBuffer.Append(buf, lineBreakPos + 1, chunkLength - (lineBreakPos + 1)); + } + } + } + + private void OnChunk(ArraySegment chunk) + { + var dlg = OnReceivedChunk; + dlg?.Invoke(chunk); + } + + private void OnCompleteLine(string line) + { + var dlg = OnReceivedLine; + dlg?.Invoke(line); + } + + private void OnClosed() + { + var dlg = OnStreamClosed; + dlg?.Invoke(); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/EventedStreamStringReader.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/EventedStreamStringReader.cs new file mode 100644 index 0000000000..efe7c72efa --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/EventedStreamStringReader.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; + +namespace Microsoft.AspNetCore.NodeServices.Util +{ + /// + /// Captures the completed-line notifications from a , + /// combining the data into a single . + /// + internal class EventedStreamStringReader : IDisposable + { + private EventedStreamReader _eventedStreamReader; + private bool _isDisposed; + private StringBuilder _stringBuilder = new StringBuilder(); + + public EventedStreamStringReader(EventedStreamReader eventedStreamReader) + { + _eventedStreamReader = eventedStreamReader + ?? throw new ArgumentNullException(nameof(eventedStreamReader)); + _eventedStreamReader.OnReceivedLine += OnReceivedLine; + } + + public string ReadAsString() => _stringBuilder.ToString(); + + private void OnReceivedLine(string line) => _stringBuilder.AppendLine(line); + + public void Dispose() + { + if (!_isDisposed) + { + _eventedStreamReader.OnReceivedLine -= OnReceivedLine; + _isDisposed = true; + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/LoggerFinder.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/LoggerFinder.cs new file mode 100644 index 0000000000..e3a735fd5b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/LoggerFinder.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; + +namespace Microsoft.AspNetCore.SpaServices.Util +{ + internal static class LoggerFinder + { + public static ILogger GetOrCreateLogger( + IApplicationBuilder appBuilder, + string logCategoryName) + { + // If the DI system gives us a logger, use it. Otherwise, set up a default one. + var loggerFactory = appBuilder.ApplicationServices.GetService(); + var logger = loggerFactory != null + ? loggerFactory.CreateLogger(logCategoryName) +#pragma warning disable CS0618 // Type or member is obsolete + : new ConsoleLogger(logCategoryName, null, false); +#pragma warning restore CS0618 + return logger; + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/TaskTimeoutExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/TaskTimeoutExtensions.cs new file mode 100644 index 0000000000..fe4d6b9c97 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/TaskTimeoutExtensions.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.SpaServices.Extensions.Util +{ + internal static class TaskTimeoutExtensions + { + public static async Task WithTimeout(this Task task, TimeSpan timeoutDelay, string message) + { + if (task == await Task.WhenAny(task, Task.Delay(timeoutDelay))) + { + task.Wait(); // Allow any errors to propagate + } + else + { + throw new TimeoutException(message); + } + } + + public static async Task WithTimeout(this Task task, TimeSpan timeoutDelay, string message) + { + if (task == await Task.WhenAny(task, Task.Delay(timeoutDelay))) + { + return task.Result; + } + else + { + throw new TimeoutException(message); + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/TcpPortFinder.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/TcpPortFinder.cs new file mode 100644 index 0000000000..1682ff7dd9 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/TcpPortFinder.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Net; +using System.Net.Sockets; + +namespace Microsoft.AspNetCore.SpaServices.Util +{ + internal static class TcpPortFinder + { + public static int FindAvailablePort() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + try + { + return ((IPEndPoint)listener.LocalEndpoint).Port; + } + finally + { + listener.Stop(); + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/baseline.netcore.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/baseline.netcore.json new file mode 100644 index 0000000000..21d6857e98 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices.Extensions/baseline.netcore.json @@ -0,0 +1,825 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.SpaServices.Extensions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.SpaStaticFilesExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddSpaStaticFiles", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "configuration", + "Type": "System.Action", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UseSpaStaticFiles", + "Parameters": [ + { + "Name": "applicationBuilder", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UseSpaStaticFiles", + "Parameters": [ + { + "Name": "applicationBuilder", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Builder.StaticFileOptions" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.SpaPrerenderingExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseSpaPrerendering", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + }, + { + "Name": "configuration", + "Type": "System.Action" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.SpaPrerenderingOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_BootModuleBuilder", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_BootModuleBuilder", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_BootModulePath", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_BootModulePath", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExcludeUrls", + "Parameters": [], + "ReturnType": "System.String[]", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ExcludeUrls", + "Parameters": [ + { + "Name": "value", + "Type": "System.String[]" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupplyData", + "Parameters": [], + "ReturnType": "System.Action>", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SupplyData", + "Parameters": [ + { + "Name": "value", + "Type": "System.Action>" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.SpaProxyingExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseProxyToSpaDevelopmentServer", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + }, + { + "Name": "baseUri", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UseProxyToSpaDevelopmentServer", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + }, + { + "Name": "baseUri", + "Type": "System.Uri" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UseProxyToSpaDevelopmentServer", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + }, + { + "Name": "baseUriTaskFactory", + "Type": "System.Func>" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.SpaApplicationBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseSpa", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "configuration", + "Type": "System.Action" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.ISpaBuilder", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ApplicationBuilder", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.SpaServices.SpaOptions", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.SpaOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_DefaultPage", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_DefaultPage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.PathString" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_DefaultPageStaticFileOptions", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Builder.StaticFileOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_DefaultPageStaticFileOptions", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Builder.StaticFileOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SourcePath", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SourcePath", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_StartupTimeout", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_StartupTimeout", + "Parameters": [ + { + "Name": "value", + "Type": "System.TimeSpan" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.StaticFiles.ISpaStaticFileProvider", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_FileProvider", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.FileProviders.IFileProvider", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.StaticFiles.SpaStaticFilesOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RootPath", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RootPath", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer.ReactDevelopmentServerMiddlewareExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseReactDevelopmentServer", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + }, + { + "Name": "npmScript", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Build", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.AngularCli.AngularCliBuilder", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Build", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "npmScript", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.AngularCli.AngularCliMiddlewareExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseAngularCliServer", + "Parameters": [ + { + "Name": "spaBuilder", + "Type": "Microsoft.AspNetCore.SpaServices.ISpaBuilder" + }, + { + "Name": "npmScript", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.Util.EventedStreamReader+OnReceivedChunkHandler", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "BaseType": "System.MulticastDelegate", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Invoke", + "Parameters": [ + { + "Name": "chunk", + "Type": "System.ArraySegment" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "BeginInvoke", + "Parameters": [ + { + "Name": "chunk", + "Type": "System.ArraySegment" + }, + { + "Name": "callback", + "Type": "System.AsyncCallback" + }, + { + "Name": "object", + "Type": "System.Object" + } + ], + "ReturnType": "System.IAsyncResult", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "EndInvoke", + "Parameters": [ + { + "Name": "result", + "Type": "System.IAsyncResult" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "object", + "Type": "System.Object" + }, + { + "Name": "method", + "Type": "System.IntPtr" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.Util.EventedStreamReader+OnReceivedLineHandler", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "BaseType": "System.MulticastDelegate", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Invoke", + "Parameters": [ + { + "Name": "line", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "BeginInvoke", + "Parameters": [ + { + "Name": "line", + "Type": "System.String" + }, + { + "Name": "callback", + "Type": "System.AsyncCallback" + }, + { + "Name": "object", + "Type": "System.Object" + } + ], + "ReturnType": "System.IAsyncResult", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "EndInvoke", + "Parameters": [ + { + "Name": "result", + "Type": "System.IAsyncResult" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "object", + "Type": "System.Object" + }, + { + "Name": "method", + "Type": "System.IntPtr" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.NodeServices.Util.EventedStreamReader+OnStreamClosedHandler", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "BaseType": "System.MulticastDelegate", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Invoke", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "BeginInvoke", + "Parameters": [ + { + "Name": "callback", + "Type": "System.AsyncCallback" + }, + { + "Name": "object", + "Type": "System.Object" + } + ], + "ReturnType": "System.IAsyncResult", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "EndInvoke", + "Parameters": [ + { + "Name": "result", + "Type": "System.IAsyncResult" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "object", + "Type": "System.Object" + }, + { + "Name": "method", + "Type": "System.IntPtr" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/.gitignore new file mode 100644 index 0000000000..c6958891dd --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/node_modules/ diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Content/Node/prerenderer.js b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Content/Node/prerenderer.js new file mode 100644 index 0000000000..ffee00bd24 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Content/Node/prerenderer.js @@ -0,0 +1,224 @@ +(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(1); + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +var path = __webpack_require__(2); +// Separate declaration and export just to add type checking on function signature +exports.renderToString = renderToStringImpl; +// This function is invoked by .NET code (via NodeServices). Its job is to hand off execution to the application's +// prerendering boot function. It can operate in two modes: +// [1] Legacy mode +// This is for backward compatibility with projects created with templates older than the generator version 0.6.0. +// In this mode, we don't really do anything here - we just load the 'aspnet-prerendering' NPM module (which must +// exist in node_modules, and must be v1.x (not v2+)), and pass through all the parameters to it. Code in +// 'aspnet-prerendering' v1.x will locate the boot function and invoke it. +// The drawback to this mode is that, for it to work, you have to deploy node_modules to production. +// [2] Current mode +// This is for projects created with the Yeoman generator 0.6.0+ (or projects manually updated). In this mode, +// we don't invoke 'require' at runtime at all. All our dependencies are bundled into the NuGet package, so you +// don't have to deploy node_modules to production. +// To determine whether we're in mode [1] or [2], the code locates your prerendering boot function, and checks whether +// a certain flag is attached to the function instance. +function renderToStringImpl(callback, applicationBasePath, bootModule, absoluteRequestUrl, requestPathAndQuery, customDataParameter, overrideTimeoutMilliseconds) { + try { + var forceLegacy = isLegacyAspNetPrerendering(); + var renderToStringFunc = !forceLegacy && findRenderToStringFunc(applicationBasePath, bootModule); + var isNotLegacyMode = renderToStringFunc && renderToStringFunc['isServerRenderer']; + if (isNotLegacyMode) { + // Current (non-legacy) mode - we invoke the exported function directly (instead of going through aspnet-prerendering) + // It's type-safe to just apply the incoming args to this function, because we already type-checked that it's a RenderToStringFunc, + // just like renderToStringImpl itself is. + renderToStringFunc.apply(null, arguments); + } + else { + // Legacy mode - just hand off execution to 'aspnet-prerendering' v1.x, which must exist in node_modules at runtime + var aspNetPrerenderingV1RenderToString = __webpack_require__(3).renderToString; + if (aspNetPrerenderingV1RenderToString) { + aspNetPrerenderingV1RenderToString(callback, applicationBasePath, bootModule, absoluteRequestUrl, requestPathAndQuery, customDataParameter, overrideTimeoutMilliseconds); + } + else { + callback('If you use aspnet-prerendering >= 2.0.0, you must update your server-side boot module to call createServerRenderer. ' + + 'Either update your boot module code, or revert to aspnet-prerendering version 1.x'); + } + } + } + catch (ex) { + // Make sure loading errors are reported back to the .NET part of the app + callback('Prerendering failed because of error: ' + + ex.stack + + '\nCurrent directory is: ' + + process.cwd()); + } +} +; +function findBootModule(applicationBasePath, bootModule) { + var bootModuleNameFullPath = path.resolve(applicationBasePath, bootModule.moduleName); + if (bootModule.webpackConfig) { + // If you're using asp-prerender-webpack-config, you're definitely in legacy mode + return null; + } + else { + return require(bootModuleNameFullPath); + } +} +function findRenderToStringFunc(applicationBasePath, bootModule) { + // First try to load the module + var foundBootModule = findBootModule(applicationBasePath, bootModule); + if (foundBootModule === null) { + return null; // Must be legacy mode + } + // Now try to pick out the function they want us to invoke + var renderToStringFunc; + if (bootModule.exportName) { + // Explicitly-named export + renderToStringFunc = foundBootModule[bootModule.exportName]; + } + else if (typeof foundBootModule !== 'function') { + // TypeScript-style default export + renderToStringFunc = foundBootModule["default"]; + } + else { + // Native default export + renderToStringFunc = foundBootModule; + } + // Validate the result + if (typeof renderToStringFunc !== 'function') { + if (bootModule.exportName) { + throw new Error("The module at " + bootModule.moduleName + " has no function export named " + bootModule.exportName + "."); + } + else { + throw new Error("The module at " + bootModule.moduleName + " does not export a default function, and you have not specified which export to invoke."); + } + } + return renderToStringFunc; +} +function isLegacyAspNetPrerendering() { + var version = getAspNetPrerenderingPackageVersion(); + return version && /^1\./.test(version); +} +function getAspNetPrerenderingPackageVersion() { + try { + var packageEntryPoint = require.resolve('aspnet-prerendering'); + var packageDir = path.dirname(packageEntryPoint); + var packageJsonPath = path.join(packageDir, 'package.json'); + var packageJson = require(packageJsonPath); + return packageJson.version.toString(); + } + catch (ex) { + // Implies aspnet-prerendering isn't in node_modules at all (or node_modules itself doesn't exist, + // which will be the case in production based on latest templates). + return null; + } +} + + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +module.exports = require("path"); + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +module.exports = require("aspnet-prerendering"); + +/***/ }) +/******/ ]))); \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Content/Node/webpack-dev-middleware.js b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Content/Node/webpack-dev-middleware.js new file mode 100644 index 0000000000..78d03802c2 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Content/Node/webpack-dev-middleware.js @@ -0,0 +1,133 @@ +(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 4); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */, +/* 1 */, +/* 2 */, +/* 3 */, +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(5); + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +exports.__esModule = true; +// Pass through the invocation to the 'aspnet-webpack' package, verifying that it can be loaded +function createWebpackDevServer(callback) { + var aspNetWebpack; + try { + aspNetWebpack = __webpack_require__(6); + } + catch (ex) { + // Developers sometimes have trouble with badly-configured Node installations, where it's unable + // to find node_modules. Or they accidentally fail to deploy node_modules, or even to run 'npm install'. + // Make sure such errors are reported back to the .NET part of the app. + callback('Webpack dev middleware failed because of an error while loading \'aspnet-webpack\'. Error was: ' + + ex.stack + + '\nCurrent directory is: ' + + process.cwd()); + return; + } + return aspNetWebpack.createWebpackDevServer.apply(this, arguments); +} +exports.createWebpackDevServer = createWebpackDevServer; + + +/***/ }), +/* 6 */ +/***/ (function(module, exports) { + +module.exports = require("aspnet-webpack"); + +/***/ }) +/******/ ]))); \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj new file mode 100644 index 0000000000..15cf81290e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj @@ -0,0 +1,27 @@ + + + + Helpers for building single-page applications on ASP.NET MVC Core. + netstandard2.0 + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/DefaultSpaPrerenderer.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/DefaultSpaPrerenderer.cs new file mode 100644 index 0000000000..946f96d360 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/DefaultSpaPrerenderer.cs @@ -0,0 +1,49 @@ +using System.Threading; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.NodeServices; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.SpaServices.Prerendering +{ + /// + /// Default implementation of a DI service that provides convenient access to + /// server-side prerendering APIs. This is an alternative to prerendering via + /// the asp-prerender-module tag helper. + /// + internal class DefaultSpaPrerenderer : ISpaPrerenderer + { + private readonly string _applicationBasePath; + private readonly CancellationToken _applicationStoppingToken; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly INodeServices _nodeServices; + + public DefaultSpaPrerenderer( + INodeServices nodeServices, + IApplicationLifetime applicationLifetime, + IHostingEnvironment hostingEnvironment, + IHttpContextAccessor httpContextAccessor) + { + _applicationBasePath = hostingEnvironment.ContentRootPath; + _applicationStoppingToken = applicationLifetime.ApplicationStopping; + _httpContextAccessor = httpContextAccessor; + _nodeServices = nodeServices; + } + + public Task RenderToString( + string moduleName, + string exportName = null, + object customDataParameter = null, + int timeoutMilliseconds = default(int)) + { + return Prerenderer.RenderToString( + _applicationBasePath, + _nodeServices, + _applicationStoppingToken, + new JavaScriptModuleExport(moduleName) { ExportName = exportName }, + _httpContextAccessor.HttpContext, + customDataParameter, + timeoutMilliseconds); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/ISpaPrerenderer.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/ISpaPrerenderer.cs new file mode 100644 index 0000000000..183d4ae632 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/ISpaPrerenderer.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.SpaServices.Prerendering +{ + /// + /// Represents a service that can perform server-side prerendering for + /// JavaScript-based Single Page Applications. This is an alternative + /// to using the 'asp-prerender-module' tag helper. + /// + public interface ISpaPrerenderer + { + /// + /// Invokes JavaScript code to perform server-side prerendering for a + /// Single-Page Application. This is an alternative to using the + /// 'asp-prerender-module' tag helper. + /// + /// The JavaScript module that exports a prerendering function. + /// The name of the export from the JavaScript module, if it is not the default export. + /// An optional JSON-serializable object to pass to the JavaScript prerendering function. + /// If specified, the prerendering task will time out after this duration if not already completed. + /// + Task RenderToString( + string moduleName, + string exportName = null, + object customDataParameter = null, + int timeoutMilliseconds = default(int)); + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/JavaScriptModuleExport.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/JavaScriptModuleExport.cs new file mode 100644 index 0000000000..97456b653d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/JavaScriptModuleExport.cs @@ -0,0 +1,30 @@ +using System; + +namespace Microsoft.AspNetCore.SpaServices.Prerendering +{ + /// + /// Describes how to find the JavaScript code that performs prerendering. + /// + public class JavaScriptModuleExport + { + /// + /// Creates a new instance of . + /// + /// The path to the JavaScript module containing prerendering code. + public JavaScriptModuleExport(string moduleName) + { + ModuleName = moduleName; + } + + /// + /// Specifies the path to the JavaScript module containing prerendering code. + /// + public string ModuleName { get; private set; } + + /// + /// If set, specifies the name of the CommonJS export that is the prerendering function to execute. + /// If not set, the JavaScript module's default CommonJS export must itself be the prerendering function. + /// + public string ExportName { get; set; } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs new file mode 100644 index 0000000000..ffa982e0dc --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs @@ -0,0 +1,126 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace Microsoft.AspNetCore.SpaServices.Prerendering +{ + /// + /// A tag helper for prerendering JavaScript applications on the server. + /// + [HtmlTargetElement(Attributes = PrerenderModuleAttributeName)] + public class PrerenderTagHelper : TagHelper + { + private const string PrerenderModuleAttributeName = "asp-prerender-module"; + private const string PrerenderExportAttributeName = "asp-prerender-export"; + private const string PrerenderDataAttributeName = "asp-prerender-data"; + private const string PrerenderTimeoutAttributeName = "asp-prerender-timeout"; + private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI + + private readonly string _applicationBasePath; + private readonly CancellationToken _applicationStoppingToken; + private readonly INodeServices _nodeServices; + + /// + /// Creates a new instance of . + /// + /// The . + public PrerenderTagHelper(IServiceProvider serviceProvider) + { + var hostEnv = (IHostingEnvironment) serviceProvider.GetService(typeof(IHostingEnvironment)); + _nodeServices = (INodeServices) serviceProvider.GetService(typeof(INodeServices)) ?? _fallbackNodeServices; + _applicationBasePath = hostEnv.ContentRootPath; + + var applicationLifetime = (IApplicationLifetime) serviceProvider.GetService(typeof(IApplicationLifetime)); + _applicationStoppingToken = applicationLifetime.ApplicationStopping; + + // Consider removing the following. Having it means you can get away with not putting app.AddNodeServices() + // in your startup file, but then again it might be confusing that you don't need to. + if (_nodeServices == null) + { + _nodeServices = _fallbackNodeServices = NodeServicesFactory.CreateNodeServices( + new NodeServicesOptions(serviceProvider)); + } + } + + /// + /// Specifies the path to the JavaScript module containing prerendering code. + /// + [HtmlAttributeName(PrerenderModuleAttributeName)] + public string ModuleName { get; set; } + + /// + /// If set, specifies the name of the CommonJS export that is the prerendering function to execute. + /// If not set, the JavaScript module's default CommonJS export must itself be the prerendering function. + /// + [HtmlAttributeName(PrerenderExportAttributeName)] + public string ExportName { get; set; } + + /// + /// An optional JSON-serializable parameter to be supplied to the prerendering code. + /// + [HtmlAttributeName(PrerenderDataAttributeName)] + public object CustomDataParameter { get; set; } + + /// + /// The maximum duration to wait for prerendering to complete. + /// + [HtmlAttributeName(PrerenderTimeoutAttributeName)] + public int TimeoutMillisecondsParameter { get; set; } + + /// + /// The . + /// + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext ViewContext { get; set; } + + /// + /// Executes the tag helper to perform server-side prerendering. + /// + /// The . + /// The . + /// A representing the operation. + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + var result = await Prerenderer.RenderToString( + _applicationBasePath, + _nodeServices, + _applicationStoppingToken, + new JavaScriptModuleExport(ModuleName) + { + ExportName = ExportName + }, + ViewContext.HttpContext, + CustomDataParameter, + TimeoutMillisecondsParameter); + + if (!string.IsNullOrEmpty(result.RedirectUrl)) + { + // It's a redirection + ViewContext.HttpContext.Response.Redirect(result.RedirectUrl); + return; + } + + if (result.StatusCode.HasValue) + { + ViewContext.HttpContext.Response.StatusCode = result.StatusCode.Value; + } + + // It's some HTML to inject + output.Content.SetHtmlContent(result.Html); + + // Also attach any specified globals to the 'window' object. This is useful for transferring + // general state between server and client. + var globalsScript = result.CreateGlobalsAssignmentScript(); + if (!string.IsNullOrEmpty(globalsScript)) + { + output.PostElement.SetHtmlContent($""); + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs new file mode 100644 index 0000000000..4e2fb16a42 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs @@ -0,0 +1,103 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.SpaServices.Prerendering +{ + /// + /// Performs server-side prerendering by invoking code in Node.js. + /// + public static class Prerenderer + { + private static readonly object CreateNodeScriptLock = new object(); + + private static StringAsTempFile NodeScript; + + internal static Task RenderToString( + string applicationBasePath, + INodeServices nodeServices, + CancellationToken applicationStoppingToken, + JavaScriptModuleExport bootModule, + HttpContext httpContext, + object customDataParameter, + int timeoutMilliseconds) + { + // We want to pass the original, unencoded incoming URL data through to Node, so that + // server-side code has the same view of the URL as client-side code (on the client, + // location.pathname returns an unencoded string). + // The following logic handles special characters in URL paths in the same way that + // Node and client-side JS does. For example, the path "/a=b%20c" gets passed through + // unchanged (whereas other .NET APIs do change it - Path.Value will return it as + // "/a=b c" and Path.ToString() will return it as "/a%3db%20c") + var requestFeature = httpContext.Features.Get(); + var unencodedPathAndQuery = requestFeature.RawTarget; + + var request = httpContext.Request; + var unencodedAbsoluteUrl = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}"; + + return RenderToString( + applicationBasePath, + nodeServices, + applicationStoppingToken, + bootModule, + unencodedAbsoluteUrl, + unencodedPathAndQuery, + customDataParameter, + timeoutMilliseconds, + request.PathBase.ToString()); + } + + /// + /// Performs server-side prerendering by invoking code in Node.js. + /// + /// The root path to your application. This is used when resolving project-relative paths. + /// The instance of that will be used to invoke JavaScript code. + /// A token that indicates when the host application is stopping. + /// The path to the JavaScript file containing the prerendering logic. + /// The URL of the currently-executing HTTP request. This is supplied to the prerendering code. + /// The path and query part of the URL of the currently-executing HTTP request. This is supplied to the prerendering code. + /// An optional JSON-serializable parameter to be supplied to the prerendering code. + /// The maximum duration to wait for prerendering to complete. + /// The PathBase for the currently-executing HTTP request. + /// + public static Task RenderToString( + string applicationBasePath, + INodeServices nodeServices, + CancellationToken applicationStoppingToken, + JavaScriptModuleExport bootModule, + string requestAbsoluteUrl, + string requestPathAndQuery, + object customDataParameter, + int timeoutMilliseconds, + string requestPathBase) + { + return nodeServices.InvokeExportAsync( + GetNodeScriptFilename(applicationStoppingToken), + "renderToString", + applicationBasePath, + bootModule, + requestAbsoluteUrl, + requestPathAndQuery, + customDataParameter, + timeoutMilliseconds, + requestPathBase); + } + + private static string GetNodeScriptFilename(CancellationToken applicationStoppingToken) + { + lock(CreateNodeScriptLock) + { + if (NodeScript == null) + { + var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js"); + NodeScript = new StringAsTempFile(script, applicationStoppingToken); // Will be cleaned up on process exit + } + } + + return NodeScript.FileName; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderingServiceCollectionExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderingServiceCollectionExtensions.cs new file mode 100644 index 0000000000..d6a674396f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderingServiceCollectionExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.SpaServices.Prerendering; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up prerendering features in an . + /// + public static class PrerenderingServiceCollectionExtensions + { + /// + /// Configures the dependency injection system to supply an implementation + /// of . + /// + /// The . + public static void AddSpaPrerenderer(this IServiceCollection serviceCollection) + { + serviceCollection.AddHttpContextAccessor(); + serviceCollection.AddSingleton(); + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/RenderToStringResult.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/RenderToStringResult.cs new file mode 100644 index 0000000000..1a2e156354 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Prerendering/RenderToStringResult.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Text; + +namespace Microsoft.AspNetCore.SpaServices.Prerendering +{ + /// + /// Describes the prerendering result returned by JavaScript code. + /// + public class RenderToStringResult + { + /// + /// If set, specifies JSON-serializable data that should be added as a set of global JavaScript variables in the document. + /// This can be used to transfer arbitrary data from server-side prerendering code to client-side code (for example, to + /// transfer the state of a Redux store). + /// + public JObject Globals { get; set; } + + /// + /// The HTML generated by the prerendering logic. + /// + public string Html { get; set; } + + /// + /// If set, specifies that instead of rendering HTML, the response should be an HTTP redirection to this URL. + /// This can be used if the prerendering code determines that the requested URL would lead to a redirection according + /// to the SPA's routing configuration. + /// + public string RedirectUrl { get; set; } + + /// + /// If set, specifies the HTTP status code that should be sent back with the server response. + /// + public int? StatusCode { get; set; } + + /// + /// Constructs a block of JavaScript code that assigns data from the + /// property to the global namespace. + /// + /// A block of JavaScript code. + public string CreateGlobalsAssignmentScript() + { + if (Globals == null) + { + return string.Empty; + } + + var stringBuilder = new StringBuilder(); + + foreach (var property in Globals.Properties()) + { + stringBuilder.AppendFormat("window.{0} = {1};", + property.Name, + property.Value.ToString(Formatting.None)); + } + + return stringBuilder.ToString(); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/README.md b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/README.md new file mode 100644 index 0000000000..e7622a4355 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/README.md @@ -0,0 +1,822 @@ +# Microsoft.AspNetCore.SpaServices + +If you're building an ASP.NET Core application, and want to use Angular, React, Knockout, or another single-page app (SPA) framework, this NuGet package contains useful infrastructure for you. + +This package enables: + + * [**Server-side prerendering**](#server-side-prerendering) for *universal* (a.k.a. *isomorphic*) applications, where your Angular / React / etc. components are first rendered on the server, and then transferred to the client where execution continues + * [**Webpack middleware**](#webpack-dev-middleware) so that, during development, any webpack-built resources will be generated on demand, without you having to run webpack manually or compile files to disk + * [**Hot module replacement**](#webpack-hot-module-replacement) so that, during development, your code and markup changes will be pushed to your browser and updated in the running application automatically, without even needing to reload the page + * [**Routing helpers**](#routing-helper-mapspafallbackroute) for integrating server-side routing with client-side routing + +Behind the scenes, it uses the [`Microsoft.AspNetCore.NodeServices`](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.NodeServices) package as a fast and robust way to invoke Node.js-hosted code from ASP.NET Core at runtime. + +### Requirements + +* [Node.js](https://nodejs.org/en/) + * To test this is installed and can be found, run `node -v` on a command line + * Note: If you're deploying to an Azure web site, you don't need to do anything here - Node is already installed and available in the server environments +* [.NET Core](https://dot.net), version 1.0 RC2 or later + +### Installation into existing projects + + * Install the `Microsoft.AspNetCore.SpaServices` NuGet package + * Run `dotnet restore` (or if you use Visual Studio, just wait a moment - it will restore dependencies automatically) + * Install supporting NPM packages for the features you'll be using: + * For **server-side prerendering**, install `aspnet-prerendering` + * For **server-side prerendering with Webpack build support**, also install `aspnet-webpack` + * For **webpack dev middleware**, install `aspnet-webpack` + * For **webpack dev middleware with hot module replacement**, also install `webpack-hot-middleware` + * For **webpack dev middleware with React hot module replacement**, also install `aspnet-webpack-react` + + For example, run `npm install --save aspnet-prerendering aspnet-webpack` to install `aspnet-prerendering` and `aspnet-webpack`. + + +### Creating entirely new projects + +If you're starting from scratch, you might prefer to use the `aspnetcore-spa` Yeoman generator to get a ready-to-go starting point using your choice of client-side framework. This includes `Microsoft.AspNetCore.SpaServices` along with everything configured for webpack middleware, server-side prerendering, etc. + +See: [Getting started with the aspnetcore-spa generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/) + +Also, if you want to debug projects created with the aspnetcore-spa generator, see [Debugging your projects](#debugging-your-projects) + +## Server-side prerendering + +The `SpaServices` package isn't tied to any particular client-side framework, and it doesn't force you to set up your client-side application in any one particular style. So, `SpaServices` doesn't contain hard-coded logic for rendering Angular / React / etc. components. + +Instead, what `SpaServices` offers is ASP.NET Core APIs that know how to invoke a JavaScript function that you supply, passing through context information that you'll need for server-side prerendering, and then injects the resulting HTML string into your rendered page. In this document, you'll find examples of setting this up to render Angular and React components. + +### 1. Enable the asp-prerender-* tag helpers + +Make sure you've installed into your project: + + * The `Microsoft.AspNetCore.SpaServices` NuGet package, version 1.1.0-* or later + * The `aspnet-prerendering` NPM package, version 2.0.1 or later + +Together these contain the server-side and client-side library code you'll need. Now go to your `Views/_ViewImports.cshtml` file, and add the following line: + + @addTagHelper "*, Microsoft.AspNetCore.SpaServices" + +### 2. Use asp-prerender-* in a view + +Choose a place in one of your MVC views where you want to prerender a SPA component. For example, open `Views/Home/Index.cshtml`, and add markup like the following: + +
+ +If you run your application now, and browse to whatever page renders the view you just edited, you should get an error similar to the following (assuming you're running in *Development* mode so you can see the error information): *Error: Cannot find module 'some/directory/ClientApp/boot-server'*. You've told the prerendering tag helper to execute code from a JavaScript module called `boot-server`, but haven't yet supplied any such module! + +### 3. Supplying JavaScript code to perform prerendering + +Create a JavaScript file at the path matching the `asp-prerender-module` value you specified above. In this example, that means creating a folder called `ClientApp` at the root of your project, and creating a file inside it called `boot-server.js`. Try putting the following into it: + +```javascript +var prerendering = require('aspnet-prerendering'); + +module.exports = prerendering.createServerRenderer(function(params) { + return new Promise(function (resolve, reject) { + var result = '

Hello world!

' + + '

Current time in Node is: ' + new Date() + '

' + + '

Request path is: ' + params.location.path + '

' + + '

Absolute URL is: ' + params.absoluteUrl + '

'; + + resolve({ html: result }); + }); +}); +``` + +If you try running your app now, you should see the HTML snippet generated by your JavaScript getting injected into your page. + +As you can see, your JavaScript code receives context information (such as the URL being requested), and returns a `Promise` so that it can asynchronously supply the markup to be injected into the page. You can put whatever logic you like here, but typically you'll want to execute a component from your Angular / React / etc. application. + +**Passing data from .NET code into JavaScript code** + +If you want to supply additional data to the JavaScript function that performs your prerendering, you can use the `asp-prerender-data` attribute. You can give any value as long as it's JSON-serializable. Bear in mind that it will be serialized and sent as part of the remote procedure call (RPC) to Node.js, so avoid trying to pass massive amounts of data. + +For example, in your `cshtml`, + +
+ +Now in your JavaScript prerendering function, you can access this data by reading `params.data`, e.g.: + +```javascript +var prerendering = require('aspnet-prerendering'); + +module.exports = prerendering.createServerRenderer(function(params) { + return new Promise(function (resolve, reject) { + var result = '

Hello world!

' + + '

Is gold user: ' + params.data.isGoldUser + '

' + + '

Number of cookies: ' + params.data.cookies.length + '

'; + + resolve({ html: result }); + }); +}); +``` + +Notice that the property names are received in JavaScript-style casing (e.g., `isGoldUser`) even though they were sent in C#-style casing (e.g., `IsGoldUser`). This is because of how the JSON serialization is configured by default. + +**Passing data from server-side to client-side code** + +If, as well as returning HTML, you also want to pass some contextual data from your server-side code to your client-side code, you can supply a `globals` object alongside the initial `html`, e.g.: + +```javascript +resolve({ + html: result, + globals: { + albumsList: someDataHere, + userData: someMoreDataHere + } +}); +``` + +When the `aspnet-prerender-*` tag helper emits this result into the document, as well as injecting the `html` string, it will also emit code that populates `window.albumsList` and `window.userData` with JSON-serialized copies of the objects you passed. + +This can be useful if, for example, you want to avoid loading the same data twice (once on the server and once on the client). + +### 4. Enabling webpack build tooling + +Of course, rather than writing your `boot-server` module and your entire SPA in plain ES5 JavaScript, it's quite likely that you'll want to write your client-side code in TypeScript or at least ES2015 code. To enable this, you need to set up a build system. + +#### Example: Configuring Webpack to build TypeScript + +Let's say you want to write your boot module and SPA code in TypeScript, and build it using Webpack. First ensure that `webpack` is installed, along with the libraries needed for TypeScript compilation: + + npm install -g webpack + npm install --save ts-loader typescript + +Next, create a file `webpack.config.js` at the root of your project, containing: + +```javascript +var path = require('path'); + +module.exports = { + entry: { 'main-server': './ClientApp/boot-server.ts' }, + resolve: { extensions: [ '', '.js', '.ts' ] }, + output: { + path: path.join(__dirname, './ClientApp/dist'), + filename: '[name].js', + libraryTarget: 'commonjs' + }, + module: { + loaders: [ + { test: /\.ts$/, loader: 'ts-loader' } + ] + }, + target: 'node', + devtool: 'inline-source-map' +}; +``` + +This tells webpack that it should compile `.ts` files using TypeScript, and that when looking for modules by name (e.g., `boot-server`), it should also find files with `.js` and `.ts` extensions. + +If you don't already have a `tsconfig.json` file at the root of your project, add one now. Make sure your `tsconfig.json` includes `"es6"` in its `"lib"` array so that TypeScript knows about intrinsics such as `Promise`. Here's an example `tsconfig.json`: + +```json +{ + "compilerOptions": { + "moduleResolution": "node", + "target": "es5", + "sourceMap": true, + "lib": [ "es6", "dom" ] + }, + "exclude": [ "bin", "node_modules" ] +} +``` + +Now you can delete `ClientApp/boot-server.js`, and in its place, create `ClientApp/boot-server.ts`, containing the TypeScript equivalent of what you had before: + +```javascript +import { createServerRenderer } from 'aspnet-prerendering'; + +export default createServerRenderer(params => { + return new Promise((resolve, reject) => { + const html = ` +

Hello world!

+

Current time in Node is: ${ new Date() }

+

Request path is: ${ params.location.path }

+

Absolute URL is: ${ params.absoluteUrl }

`; + + resolve({ html }); + }); +}); +``` + +Finally, run `webpack` on the command line to build `ClientApp/dist/main-server.js`. Then you can tell `SpaServices` to use that file for server-side prerendering. In your MVC view where you use `aspnet-prerender-module`, update the attribute value: + +
+ +Webpack is a broad and powerful tool and can do far more than just invoke the TypeScript compiler. To learn more, see the [webpack website](https://webpack.github.io/). + + +### 5(a). Prerendering Angular components + +If you're building an Angular application, you can run your components on the server inside your `boot-server.ts` file so they will be injected into the resulting web page. + +First install the NPM package `angular2-universal` - this contains infrastructure for executing Angular components inside Node.js: + +``` +npm install --save angular2-universal +``` + +Now you can use the [`angular2-universal` APIs](https://github.com/angular/universal) from your `boot-server.ts` TypeScript module to execute your Angular component on the server. The code needed for this is fairly complex, but that's unavoidable because Angular supports so many different ways of being configured, and you need to provide wiring for whatever combination of DI modules you're using. + +You can find an example `boot-server.ts` that renders arbitrary Angular components [here](https://github.com/aspnet/JavaScriptServices/blob/dev/templates/AngularSpa/ClientApp/boot-server.ts). If you use this with your own application, you might need to edit the `serverBindings` array to reference any other DI services that your Angular component depends on. + +The easiest way to get started with Angular server-side rendering on ASP.NET Core is to use the [aspnetcore-spa generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/), which creates a ready-made working starting point. + +### 5(b). Prerendering React components + +React components can be executed synchronously on the server quite easily, although asynchronous execution is tricker as described below. + +#### Setting up client-side React code + +Let's say you want to write a React component in ES2015 code. You might install the NPM modules `react react-dom babel-loader babel-preset-react babel-preset-es2015`, and then prepare Webpack to build `.jsx` files by creating `webpack.config.js` in your project root, containing: + +```javascript +var path = require('path'); + +module.exports = { + resolve: { extensions: [ '', '.js', '.jsx' ] }, + module: { + loaders: [ + { test: /\.jsx?$/, loader: 'babel-loader' } + ] + }, + entry: { + main: ['./ClientApp/react-app.jsx'], + }, + output: { + path: path.join(__dirname, 'wwwroot', 'dist'), + filename: '[name].js' + }, +}; +``` + +You will also need a `.babelrc` file in your project root, containing: + +```javascript +{ + "presets": ["es2015", "react"] +} +``` + +This is enough to be able to build ES2015 `.jsx` files via Webpack. Now you could implement a simple React component, for example the following at `ClientApp/react-app.jsx`: + +```javascript +import * as React from 'react'; + +export class HelloMessage extends React.Component +{ + render() { + return

Hello {this.props.message}!

; + } +} +``` + +... and the following code to run it in a browser at `ClientApp/boot-client.jsx`: + +```javascript +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HelloMessage } from './react-app'; + +ReactDOM.render(, document.getElementById('my-spa')); +``` + +At this stage, run `webpack` on the command line to build `wwwroot/dist/main.js`. Or, to avoid having to do this manually, you could use the `SpaServices` package to [enable Webpack dev middleware](#webpack-dev-middleware). + +You can now run your React code on the client by adding the following to one of your MVC views: + +
+ + +If you want to enable server-side prerendering too, follow the same process as described under [server-side prerendering](#server-side-prerendering). + +#### Realistic React apps and Redux + +The above example is extremely simple - it doesn't use `react-router`, and it doesn't load any data asynchronously. Real applications are likely to do both of these. + +For an example server-side boot module that knows how to evaluate `react-router` routes and render the correct React component, see [this example](https://github.com/aspnet/JavaScriptServices/blob/dev/templates/ReactReduxSpa/ClientApp/boot-server.tsx). + +Supporting asynchronous data loading involves more considerations. Unlike Angular applications that run asynchronously on the server and freely overwrite server-generated markup with client-generated markup, React strictly wants to run synchronously on the server and always produce the same markup on the server as it does on the client. + +To make this work, you most likely need some way to know in advance what data your React components will need to use, load it separately from those components, and have some way of transferring information about the loaded data from server to client. If you try to implement this in a generalized way, you'll end up reinventing something like the Flux/Redux pattern. + +To avoid inventing your own incomplete version of Flux/Redux, you probably should just use [Redux](https://github.com/reactjs/redux). This is at first a very unfamiliar and tricky-looking abstraction, but does solve all the problems around server-side execution of React apps. To get a working starting point for an ASP.NET Core site with React+Redux on the client (and server-side prerendering), see the [aspnetcore-spa generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/). + +## Webpack dev middleware + +If you're using webpack, the webpack dev middleware feature included in `Microsoft.AspNetCore.SpaServices` will streamline your development process. It intercepts requests that would match files built by webpack, and dynamically builds those files on demand. They don't need to be written to disk - they are just held in memory and served directly to the browser. + +Benefits: + + * You don't have to run `webpack` manually or set up any file watchers + * The browser is always guaranteed to receive up-to-date built output + * The built artifacts are normally served instantly or at least extremely quickly, because internally, an instance of `webpack` stays active and has partial compilation states pre-cached in memory + +It lets you work as if the browser natively understands whatever file types you are working with (e.g., TypeScript, SASS), because it's as if there's no build process to wait for. + +### Example: A simple Webpack setup that builds TypeScript + +**Note:** If you already have Webpack in your project, then you can skip this section. + +As a simple example, here's how you can set up Webpack to build TypeScript files. First install the relevant NPM packages by executing this from the root directory of your project: + +``` +npm install --save typescript ts-loader +``` + +And if you don't already have it, you'll find it useful to install the `webpack` command-line tool: + +``` +npm install -g webpack +``` + +Now add a Webpack configuration file. Create `webpack.config.js` in the root of your project, containing the following: + +```javascript +module.exports = { + resolve: { + // For modules referenced with no filename extension, Webpack will consider these extensions + extensions: [ '', '.js', '.ts' ] + }, + module: { + loaders: [ + // This example only configures Webpack to load .ts files. You can also drop in loaders + // for other file types, e.g., .coffee, .sass, .jsx, ... + { test: /\.ts$/, loader: 'ts-loader' } + ] + }, + entry: { + // The loader will follow all chains of reference from this entry point... + main: ['./ClientApp/MyApp.ts'] + }, + output: { + // ... and emit the built result in this location + path: __dirname + '/wwwroot/dist', + filename: '[name].js' + }, +}; +``` + +Now you can put some TypeScript code (minimally, just `console.log('Hello');`) at `ClientApp/MyApp.ts` and then run `webpack` from the command line to build it (and everything it references). The output will be placed in `wwwroot/dist`, so you can load and run it in a browser by adding the following to one of your views (e.g., `Views\Home\Index.cshtml`): + + + +The Webpack loader, `ts-loader`, follows all chains of reference from `MyApp.ts` and will compile all referenced TypeScript code into your output. If you want, you can create a [`tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) to control things like whether source maps will be included in the output. If you add other Webpack loaders to your `webpack.config.js`, you can even reference things like SASS from your TypeScript, and then it will get built to CSS and loaded automatically. + +So that's enough to build TypeScript. Here's where webpack dev middleware comes in to auto-build your code whenever needed (so you don't need any file watchers or to run `webpack` manually), and optionally hot module replacement (HMR) to push your changes automatically from code editor to browser without even reloading the page. + +### Example: A simple Webpack setup that builds LESS + +Following on from the preceding example that builds TypeScript, you could extend your Webpack configuration further to support building LESS. There are three major approaches to doing this: + +1. **If using Angular, use its native style loader to attach the styles to components**. This is extremely simple and is usually the right choice if you are using Angular. However it only applies to Angular components, not to any other part of the host page, so sometimes you might want to combine this technique with options 2 or 3 below. + +2. **Or, use Webpack's style loader to attach the styles at runtime**. The CSS markup will be included in your JavaScript bundles and will be attached to the document dynamically. This has certain benefits during development but isn't recommended in production. + +3. **Or, have each build write a standalone `.css` file to disk**. At runtime, load it using a regular `` tag. This is likely to be the approach you'll want for production use (at least for non-Angular applications, such as React applications) as it's the most robust and best-performing option. + +If instead of LESS you prefer SASS or another CSS preprocessor, the exact same techniques should work, but of course you'll need to replace the `less-loader` with an equivalent Webpack loader for SASS or your chosen preprocessor. + +#### Approach 1: Scoping styles to Angular components + +If you are using Angular, this is the easiest way to perform styling. It works with both server and client rendering, supports Hot Module Replacement, and robustly scopes styles to particular components (and optionally, their descendant elements). + +This repository's Angular template uses this technique to scope styles to components out of the box. It defines those styles as `.css` files. For example, its components reference `.css` files like this: + +```javascript +@Component({ + ... + styles: [require('./somecomponent.css')] +}) +export class SomeComponent { ... } +``` + +To make this work, the template has Webpack configured to inject the contents of the `.css` file as a string literal in the built file. Here's the configuration that enables this: + +```javascript +// This goes into webpack.config.js, in the module loaders array: +{ test: /\.css/, include: /ClientApp/, loader: 'raw-loader' } +``` + +Now if you want to use LESS instead of plain CSS, you just need to include a LESS loader. Run the following in a command prompt at your project root: + +``` +npm install --save less-loader less +``` + +Next, add the following loader configuration to the `loaders` array in `webpack.config.js`: + +```javascript +{ test: /\.less/, include: /ClientApp/, loader: 'raw-loader!less-loader' } +``` + +Notice how this chains together with `less-loader` (which transforms `.less` syntax to plain CSS syntax), then the `raw` loader (which turn the result into a string literal). With this in place, you can reference `.less` files from your Angular components in the obvious way: + +```javascript +@Component({ + ... + styles: [require('./somecomponent.less')] +}) +export class SomeComponent { ... } +``` + +... and your styles will be applied in both server-side and client-side rendering. + +#### Approach 2: Loading the styles using Webpack and JavaScript + +This technique works with any client-side framework (not just Angular), and can also apply styles to the entire document rather than just individual components. It's a little simpler to set up than technique 3, plus it works flawlessly with Hot Module Replacement (HMR). The downside is that it's really only good for development time, because in production you probably don't want users to wait until JavaScript is loaded before styles are applied to the page (this would mean they'd see a 'flash of unstyled content' while the page is being loaded). + +First create a `.less` file in your project. For example, create a file at `ClientApp/styles/mystyles.less` containing: + +```less +@base: #f938ab; + +h1 { + color: @base; +} +``` + +Reference this file from an `import` or `require` statement in one of your JavaScript or TypeScript files. For example, if you've got a `boot-client.ts` file, add the following near the top: + +```javascript +import './styles/mystyles.less'; +``` + +If you try to run the Webpack compiler now (e.g., via `webpack` on the command line), you'll get an error saying it doesn't know how to build `.less` files. So, it's time to install a Webpack loader for LESS (plus related NPM modules). In a command prompt at your project's root directory, run: + +``` +npm install --save less-loader less +``` + +Finally, tell Webpack to use this whenever it encounters a `.less` file. In `webpack.config.js`, add to the `loaders` array: + +``` +{ test: /\.less/, loader: 'style-loader!css-loader!less-loader' } +``` + +This means that when you `import` or `require` a `.less` file, it should pass it first to the LESS compiler to produce CSS, then the output goes to the CSS and Style loaders that know how to attach it dynamically to the page at runtime. + +That's all you need to do! Restart your site and you should see the LESS styles being applied. This technique is compatible with both source maps and Hot Module Replacement (HMR), so you can edit your `.less` files at will and see the changes appearing live in the browser. + +#### Approach 3: Building LESS to CSS files on disk + +This technique takes a little more work to set up than technique 2, and lacks compatibility with HMR. But it's much better for production use if your styles are applied to the whole page (not just elements constructed via JavaScript), because it loads the CSS independently of JavaScript. + +First add a `.less` file into your project. For example, create a file at `ClientApp/styles/mystyles.less` containing: + +```less +@base: #f938ab; + +h1 { + color: @base; +} +``` + +Reference this file from an `import` or `require` statement in one of your JavaScript or TypeScript files. For example, if you've got a `boot-client.ts` file, add the following near the top: + +```javascript +import './styles/mystyles.less'; +``` + +If you try to run the Webpack compiler now (e.g., via `webpack` on the command line), you'll get an error saying it doesn't know how to build `.less` files. So, it's time to install a Webpack loader for LESS (plus related NPM modules). In a command prompt at your project's root directory, run: + +``` +npm install --save less less-loader extract-text-webpack-plugin +``` + +Next, you can extend your Webpack configuration to handle `.less` files. In `webpack.config.js`, at the top, add: + +```javascript +var extractStyles = new (require('extract-text-webpack-plugin'))('mystyles.css'); +``` + +This creates a plugin instance that will output text to a file called `mystyles.css`. You can now compile `.less` files and emit the resulting CSS text into that file. To do so, add the following to the `loaders` array in your Webpack configuration: + +```javascript +{ test: /\.less$/, loader: extractStyles.extract('css-loader!less-loader') } +``` + +This tells Webpack that, whenever it finds a `.less` file, it should use the LESS loader to produce CSS, and then feed that CSS into the `extractStyles` object which you've already configured to write a file on disk called `mystyles.css`. Finally, for this to actually work, you need to include `extractStyles` in the list of active plugins. Just add that object to the `plugins` array in your Webpack config, e.g.: + +```javascript +plugins: [ + extractStyles, + ... leave any other plugins here ... +] +``` + +If you run `webpack` on the command line now, you should now find that it emits a new file at `dist/mystyles.css`. You can make browsers load this file simply by adding a regular `` tag. For example, in `Views/Shared/_Layout.cshtml`, add: + +```html + +``` + +**Note:** This technique (writing the built `.css` file to disk) is ideal for production use. But note that, at development time, *it does not support Hot Module Replacement (HMR)*. You will need to reload the page each time you edit your `.less` file. This is a known limitation of `extract-text-webpack-plugin`. If you have constructive opinions on how this can be improved, see the [discussion here](https://github.com/webpack/extract-text-webpack-plugin/issues/30). + +### Enabling webpack dev middleware + +First install the `Microsoft.AspNetCore.SpaServices` NuGet package and the `aspnet-webpack` NPM package, then go to your `Startup.cs` file, and **before your call to `UseStaticFiles`**, add the following: + +```csharp +if (env.IsDevelopment()) { + app.UseWebpackDevMiddleware(); +} + +// Your call to app.UseStaticFiles(); should be here +``` + +Also check your webpack configuration at `webpack.config.js`. Since `UseWebpackDevMiddleware` needs to know which incoming requests to intercept, make sure you've specified a `publicPath` value on your `output`, for example: + +```javascript +module.exports = { + // ... rest of your webpack config is here ... + + output: { + path: path.join(__dirname, 'wwwroot', 'dist'), + publicPath: '/dist/', + filename: '[name].js' + }, +}; +``` + +Now, assuming you're running in [development mode](https://docs.asp.net/en/latest/fundamentals/environments.html), any requests for files under `/dist` will be intercepted and served using Webpack dev middleware. + +**This is for development time only, not for production use (hence the `env.IsDevelopment()` check in the code above).** While you could technically remove that check and serve your content in production through the webpack middleware, it's hard to think of a good reason for doing so. For best performance, it makes sense to prebuild your client-side resources so they can be served directly from disk with no build middleware. If you use the [aspnetcore-spa generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/), you'll get a site that produces optimised static builds for production, while also supporting webpack dev middleware at development time. + +## Webpack Hot Module Replacement + +For an even more streamlined development experience, you can enhance webpack dev middleware by enabling Hot Module Replacement (HMR) support. This watches for any changes you make to source files on disk (e.g., `.ts`/`.html`/`.sass`/etc. files), and automatically rebuilds them and pushes the result into your browser window, without even needing to reload the page. + +This is *not* the same as a simple live-reload mechanism. It does not reload the page; it replaces code or markup directly in place. This is better, because it does not interfere with any state your SPA might have in memory, or any debugging session you have in progress. + +Typically, when you change a source file, the effects appear in your local browser window in under 2 seconds, even when your overall application is large. This is superbly productive, especially in multi-monitor setups. If you cause a build error (e.g., a syntax error), details of the error will appear in your browser window. When you fix it, your application will reappear, without having lost its in-memory state. + +### Enabling Hot Module Replacement + +First ensure you already have a working Webpack dev middleware setup. Then, install the `webpack-hot-middleware` NPM module: + +``` +npm install --save-dev webpack-hot-middleware +``` + +At the top of your `Startup.cs` file, add the following namespace reference: + +```csharp +using Microsoft.AspNetCore.SpaServices.Webpack; +``` + +Now amend your call to `UseWebpackDevMiddleware` as follows: + +```csharp +app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { + HotModuleReplacement = true +}); +``` + +Also, to work around a temporary issue in `SpaServices`, you must ensure that your Webpack config includes a `plugins` array, even if it's empty. For example, in `webpack.config.js`: + +```javascript +module.exports = { + // ... rest of your webpack config is here ... + + plugins: [ + // Put webpack plugins here if needed, or leave it as an empty array if not + ] +}; +``` + +Now when you load your application in a browser, you should see a message like the following in your browser console: + +``` +[HMR] connected +``` + +If you edit any of your source files that get built by webpack, the result will automatically be pushed into the browser. As for what the browser does with these updates - that's a matter of how you configure it - see below. + +**Note for TypeScript + Visual Studio users** + +If you want HMR to work correctly with TypeScript, and you use Visual Studio on Windows as an IDE (but not VS Code), then you will need to make a further configuration change. In your `.csproj` file, in one of the `` elements, add this: + + true + +This is necessary because otherwise, Visual Studio will try to auto-compile TypeScript files as you save changes to them. That default auto-compilation behavior is unhelpful in projects where you have a proper build system (e.g., Webpack), because VS doesn't know about your build system and would emit `.js` files in the wrong locations, which would in turn cause problems with your real build or deployment mechanisms. + +#### Enabling hot replacement for React components + +Webpack has built-in support for updating React components in place. To enable this, amend your `UseWebpackDevMiddleware` call further as follows: + +```csharp +app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { + HotModuleReplacement = true, + ReactHotModuleReplacement = true +}); +``` + +Also, install the NPM module `aspnet-webpack-react`, e.g.: + +``` +npm install --save-dev aspnet-webpack-react +``` + +Now if you edit any React component (e.g., in `.jsx` or `.tsx` files), the updated component will be injected into the running application, and will even preserve its in-memory state. + +**Note**: In you webpack config, be sure that your React components are loaded using `babel-loader` (and *not* just directly using `babel` or `ts-loader`), because `babel-loader` is where the HMR instrumentation is injected. For an example of HMR for React components built with TypeScript, see the [aspnetcore-spa generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/). + +#### Enabling hot replacement for other module types + +Webpack has built-in HMR support for various types of module, such as styles and React components as described above. But to support HMR for other code modules, you need to add a small block of code that calls `module.hot.accept` to receive the updated module and update the running application. + +This is [documented in detail on the Webpack site](https://webpack.github.io/docs/hot-module-replacement.html). Or to get a working HMR-enabled ASP.NET Core site with Angular, React, React+Redux, or Knockout, you can use the [aspnetcore-spa generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/). + +#### Passing options to the Webpack Hot Middleware client + +You can configure the [Webpack Hot Middleware client](https://github.com/glenjamin/webpack-hot-middleware#client) +by using the `HotModuleReplacementClientOptions` property on `WebpackDevMiddlewareOptions`: + +```csharp +app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { + HotModuleReplacement = true, + HotModuleReplacementClientOptions = new Dictionary { + { "reload", "true" }, + }, +}); +``` + +For the list of available options, please see [Webpack Hot Middleware docs](https://github.com/glenjamin/webpack-hot-middleware#client). + +**Note**: The `path` option cannot be overridden this way - it is controlled by the `HotModuleReplacementEndpoint` setting. + +## Routing helper: MapSpaFallbackRoute + +In most single-page applications, you'll want client-side routing as well as your server-side routing. Most of the time, the two routing systems work independently without interfering. However, there is one case where things get challenging: identifying 404s. + +If a request arrives for `/some/page`, and it doesn't match any server-side route, it's likely that you want to return HTML that starts up your client-side application, which probably understands the route `/some/page`. But if a request arrives for `/images/user-512.png`, and it doesn't match any server-side route or static file, it's **not** likely that your client-side application would handle it - you probably want to return a 404. + +To help distinguish between these cases, the `Microsoft.AspNetCore.SpaServices` NuGet package includes a routing helper, `MapSpaFallbackRoute`. For example, in your `Startup.cs` file's `Configure` method, you might add: + +```csharp + app.UseStaticFiles(); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + + routes.MapSpaFallbackRoute( + name: "spa-fallback", + defaults: new { controller = "Home", action = "Index" }); + }); +``` + +Since `UseStaticFiles` goes first, any requests that actually match physical files under `wwwroot` will be handled by serving that static file. + +Since the default server-side MVC route goes next, any requests that match existing controller/action pairs will be handled by invoking that action. + +Then, since `MapSpaFallbackRoute` is last, any other requests **that don't appear to be for static files** will be served by invoking the `Index` action on `HomeController`. This action's view should serve your client-side application code, allowing the client-side routing system to handle whatever URL has been requested. + +Any requests that do appear to be for static files (i.e., those that end with filename extensions), will *not* be handled by `MapSpaFallbackRoute`, and so will end up as 404s. + +This is not a perfect solution to the problem of identifying 404s, because for example `MapSpaFallbackRoute` will not match requests for `/users/albert.einstein`, because it appears to contain a filename extension (`.einstein`). If you need your SPA to handle routes like that, then don't use `MapSpaFallbackRoute` - just use a regular MVC catch-all route. But then beware that requests for unknown static files will result in your client-side app being rendered. + +## Debugging your projects + +How to attach and use a debugger depends on what code you want to debug. For details, see: + + * [How to debug your C# code that runs on the server](#debugging-your-c-code-that-runs-on-the-server) + * How to debug your JavaScript/TypeScript code: + * ... [when it's running in a browser](#debugging-your-javascripttypescript-code-when-its-running-in-a-browser) + * ... [when it's running on the server](#debugging-your-javascripttypescript-code-when-it-runs-on-the-server) (i.e., via `asp-prerender` or NodeSevices) + +### Debugging your C# code that runs on the server + +You can use any .NET debugger, for example Visual Studio's C# debugger or [Visual Studio Code's C# debugger](https://code.visualstudio.com/Docs/editor/debugging). + +### Debugging your JavaScript/TypeScript code when it's running in a browser + +**The absolute most reliable way of debugging your client-side code is to use your browser's built-in debugger.** This is much easier to make work than debugging via an IDE, plus it offers much richer insight into what's going on than your IDE will do (for example, you'll be able to inspect the DOM and capture performance profiles as well as just set breakpoints and step through code). + +If you're unfamiliar with your browser's debugging tools, then take the time to get familiar with them. You will become more productive. + +#### Using your browser's built-in debugging tools + +##### Using Chrome's developer tools for debugging + +In Chrome, with your application running in the browser, [open the developer tools](https://developer.chrome.com/devtools#access). You can now find your code: + + * In the developer tools *Sources* tab, expand folders in the hierarchy pane on the left to find the file you want + * Or, press `ctrl`+`o` (on Windows) or `cmd`+`o` on Mac, then start to type name name of the file you want to open (e.g., `counter.component.ts`) + +With source maps enabled (which is the case in the project templates in this repo), you'll be able to see your original TypeScript source code, set breakpoints on it, etc. + +##### Using Internet Explorer/Edge's developer tools (F12) for debugging + +In Internet Explorer or Edge, with your application running in the browser, open the F12 developer tools by pressing `F12`. You can now find your code: + + * In the F12 tools *Debugger* tab, expand folders in the hierarchy pane on the left to find the file you want + * Or, press `ctrl`+`o`, then start to type name name of the file you want to open (e.g., `counter.component.ts`) + +With source maps enabled (which is the case in the project templates in this repo), you'll be able to see your original TypeScript source code, set breakpoints on it, etc. + +##### Using Firefox's developer tools for debugging + +In Firefox, with your application running in the browser, open the developer tools by pressing `F12`. You can now find your code: + + * In the developer tools *Debugger* tab, expand folders in the hierarchy pane titled *Sources* towards the bottom to find the file you want + * Or, press `ctrl`+`o` (on Windows) or `cmd`+`o` on Mac, then start to type name name of the file you want to open (e.g., `counter.component.ts`) + +With source maps enabled (which is the case in the project templates in this repo), you'll be able to see your original TypeScript source code, set breakpoints on it, etc. + +##### How browser-based debugging interacts with Hot Module Replacement (HMR) + +If you're using HMR, then each time you modify a file, the Webpack dev middleware restarts your client-side application, adding a new version of each affected module, without reloading the page. This can be confusing during debugging, because any breakpoints set on the old version of the code will still be there, but they will no longer get hit, because the old version of the module is no longer in use. + +You have two options to get breakpoints that will be hit as expected: + + * **Reload the page** (e.g., by pressing `F5`). Then your existing breakpoints will be applied to the new version of the module. This is obviously the easiest solution. + * Or, if you don't want to reload the page, you can **set new breakpoints on the new version of the module**. To do this, look in your browser's debug tools' list of source files, and identify the newly-injected copy of the module you want to debug. It will typically have a suffix on its URL such as `?4a2c`, and may appear in a new top-level hierarchy entry called `webpack://`. Set a breakpoint in the newly-injected module, and it will be hit as expected as your application runs. + +#### Using Visual Studio Code's "Debugger for Chrome" extension + +If you're using Visual Studio Code and Chrome, you can set breakpoints directly on your TypeScript source code in the IDE. To do this: + +1. Install VS Code's [*Debugger for Chrome* extension](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) +2. Ensure your application server has started and can be reached with a browser (for example, run `dotnet watch run`) +3. In VS Code, open its *Debug* view (on Windows/Linux, press `ctrl`+`shift`+`d`; on Mac, press `cmd`+`shift`+`d`). +4. Press the cog icon and when prompted to *Select environment*, choose `Chrome`. VS Code will create a `launch.json` file for you. This describes how the debugger and browser should be launched. +5. Edit your new `.vscode/launch.json` file to specify the correct `url` and `webRoot` for your application. If you're using the project templates in this repo, then the values you probably want are: + * For `url`, put `"http://localhost:5000"` (but of course, change this if you're using a different port) + * For `port`, put `5000` (or your custom port number if applicable) + * For `workspace` in **both** configurations, put `"${workspaceRoot}/wwwroot"` + * This tells the debugger how URLs within your application correspond to files in your VS Code workspace. By default, ASP.NET Core projects treat `wwwroot` as the root directory for publicly-served files, so `http://localhost:5000/dist/myfile.js` corresponds to `/wwwroot/dist/myfile.js`. VS Code doesn't know about `wwwroot` unless you tell it. + * **Important:** If your VS Code window's workspace root is not the same as your ASP.NET Core project root (for example, if VS Code is opened at a higher-level directory to show both your ASP.NET Core project plus other peer-level directories), then you will need to amend `workspace` correspondingly (e.g., to `"${workspaceRoot}/SomeDir/MyAspNetProject/wwwroot"`). +6. Start the debugger: + * While still on the *Debug* view, from the dropdown near the top-left, choose "*Launch Chrome against localhost, with sourcemaps*". + * Press the *Play* icon. Your application will launch in Chrome. + * If it does nothing for a while, then eventually gives the error *Cannot connect to runtime process*, that's because you already have an instance of Chrome running. Close it first, then try again. +7. Finally, you can now set and hit breakpoints in your TypeScript code in VS Code. + +For more information about VS Code's built-in debugging facilities, [see its documentation](https://code.visualstudio.com/Docs/editor/debugging). + +Caveats: + + * The debugging interface between VS Code and Chrome occasionally has issues. If you're unable to set or hit breakpoints, or if you try to set a breakpoint but it appears in the wrong place, you may need to stop and restart the debugger (and often, the whole Chrome process). + * If you're using Hot Module Replacement (HMR), then whenever you edit a file, the breakpoints in it will no longer hit. This is because HMR loads a new version of the module into the browser, so the old code no longer runs. To fix this, you must: + * Reload the page in Chrome (e.g., by pressing `F5`) + * **Then** (and only then), remove and re-add the breakpoint in VS Code. It will now be attached to the current version of your module. Alternatively, stop and restart debugging altogether. + * If you prefer, you can use "*Attach to Chrome, with sourcemaps*" instead of launching a new Chrome instance, but this is a bit trickier: you must first start Chrome using the command-line option `--remote-debugging-port=9222`, and you must ensure there are no other tabs opened (otherwise, it might try to connect to the wrong one). + + +#### Using Visual Studio's built-in debugger for Internet Explorer + +If you're using Visual Studio on Windows, and are running your app in Internet Explorer 11 (not Edge!), then you can use VS's built-in debugger rather than Interner Explorer's F12 tools if you prefer. To do this: + + 1. In Internet Explorer, [enable script debugging](https://msdn.microsoft.com/en-us/library/ms241741\(v=vs.100\).aspx) + 2. In Visual Studio, [set the default "*Browse with*" option](http://stackoverflow.com/a/31959053) to Internet Explorer + 3. In Visual Studio, press F5 to launch your application with the debugger in Internet Explorer. + * When the page has loaded in the browser, you'll be able to set and hit breakpoints in your TypeScript source files in Visual Studio. + +Caveats: + + * If you're using Hot Module Replacement, you'll need to stop and restart the debugger any time you change a source file. VS's IE debugger does not recognise that source files might change while the debugging session is in progress. + * Realistically, you are not going to be as productive using this approach to debugging as you would be if you used your browser's built-in debugging tools. The browser's built-in debugging tools are far more effective: they are always available (you don't have to have launched your application in a special way), they better handle HMR, and they don't make your application very slow to launch. + +## Debugging your JavaScript/TypeScript code when it runs on the server + +When you're using NodeServices or the server-side prerendering feature included in the project templates in this repo, your JavaScript/TypeScript code will execute on the server in a background instance of Node.js. You can enable debugging via [V8 Inspector Integration](https://nodejs.org/api/debugger.html#debugger_v8_inspector_integration_for_node_js) on that Node.js instance. Here's how to do it. + +First, in your `Startup.cs` file, in the `ConfigureServices` method, add the following: + +``` +services.AddNodeServices(options => { + options.LaunchWithDebugging = true; + options.DebuggingPort = 9229; +}); +``` + +Now, run your application from that command line (e.g., `dotnet run`). Then in a browser visit one of your pages that causes server-side JS to execute. + +In the console, you should see all the normal trace messages appear, plus among them will be: + +``` +warn: Microsoft.AspNetCore.NodeServices[0] + Debugger listening on port 9229. +warn: Microsoft.AspNetCore.NodeServices[0] + Warning: This is an experimental feature and could change at any time. +warn: Microsoft.AspNetCore.NodeServices[0] + To start debugging, open the following URL in Chrome: +warn: Microsoft.AspNetCore.NodeServices[0] + chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +``` + +As per instructions open the URL in Chrome. Alternatively you can go to the `Sources` tab of the Dev Tools (at http://localhost:5000) and connect to the Node instance under `Threads` in the right sidebar. + +By expanding the `webpack://` entry in the sidebar, you'll be able to find your original source code (it's using source maps), and then set breakpoints in it. When you re-run your app in another browser window, your breakpoints will be hit, then you can debug the server-side execution just like you'd debug client-side execution. It looks like this: + +![screenshot from 2017-03-25 13-33-26](https://cloud.githubusercontent.com/assets/1596280/24324604/ab888a7e-115f-11e7-89d1-1586acf5e35c.png) + diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Routing/SpaRouteConstraint.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Routing/SpaRouteConstraint.cs new file mode 100644 index 0000000000..d6a1d5b227 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Routing/SpaRouteConstraint.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Microsoft.AspNetCore.SpaServices +{ + internal class SpaRouteConstraint : IRouteConstraint + { + private readonly string _clientRouteTokenName; + + public SpaRouteConstraint(string clientRouteTokenName) + { + if (string.IsNullOrEmpty(clientRouteTokenName)) + { + throw new ArgumentException("Value cannot be null or empty", nameof(clientRouteTokenName)); + } + + _clientRouteTokenName = clientRouteTokenName; + } + + public bool Match( + HttpContext httpContext, + IRouter route, + string routeKey, + RouteValueDictionary values, + RouteDirection routeDirection) + { + return !HasDotInLastSegment(values[_clientRouteTokenName] as string ?? string.Empty); + } + + private bool HasDotInLastSegment(string uri) + { + var lastSegmentStartPos = uri.LastIndexOf('/'); + return uri.IndexOf('.', lastSegmentStartPos + 1) >= 0; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Routing/SpaRouteExtensions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Routing/SpaRouteExtensions.cs new file mode 100644 index 0000000000..7838aa13b9 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Routing/SpaRouteExtensions.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.SpaServices; + +// Putting in this namespace so it's always available whenever MapRoute is + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Extension methods useful for configuring routing in a single-page application (SPA). + /// + public static class SpaRouteExtensions + { + private const string ClientRouteTokenName = "clientRoute"; + + /// + /// Configures a route that is automatically bypassed if the requested URL appears to be for a static file + /// (e.g., if it has a filename extension). + /// + /// The . + /// The route name. + /// Default route parameters. + /// Route constraints. + /// Route data tokens. + public static void MapSpaFallbackRoute( + this IRouteBuilder routeBuilder, + string name, + object defaults, + object constraints = null, + object dataTokens = null) + { + MapSpaFallbackRoute( + routeBuilder, + name, + /* templatePrefix */ null, + defaults, + constraints, + dataTokens); + } + + /// + /// Configures a route that is automatically bypassed if the requested URL appears to be for a static file + /// (e.g., if it has a filename extension). + /// + /// The . + /// The route name. + /// The template prefix. + /// Default route parameters. + /// Route constraints. + /// Route data tokens. + public static void MapSpaFallbackRoute( + this IRouteBuilder routeBuilder, + string name, + string templatePrefix, + object defaults, + object constraints = null, + object dataTokens = null) + { + var template = CreateRouteTemplate(templatePrefix); + var constraintsDict = ObjectToDictionary(constraints); + constraintsDict.Add(ClientRouteTokenName, new SpaRouteConstraint(ClientRouteTokenName)); + + routeBuilder.MapRoute(name, template, defaults, constraintsDict, dataTokens); + } + + private static string CreateRouteTemplate(string templatePrefix) + { + templatePrefix = templatePrefix ?? string.Empty; + + if (templatePrefix.Contains("?")) + { + // TODO: Consider supporting this. The {*clientRoute} part should be added immediately before the '?' + throw new ArgumentException("SPA fallback route templates don't support querystrings"); + } + + if (templatePrefix.Contains("#")) + { + throw new ArgumentException( + "SPA fallback route templates should not include # characters. The hash part of a URI does not get sent to the server."); + } + + if (templatePrefix != string.Empty && !templatePrefix.EndsWith("/")) + { + templatePrefix += "/"; + } + + return templatePrefix + $"{{*{ClientRouteTokenName}}}"; + } + + private static IDictionary ObjectToDictionary(object value) + => value as IDictionary ?? new RouteValueDictionary(value); + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/Prerenderer.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/Prerenderer.ts new file mode 100644 index 0000000000..bc49154719 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/Prerenderer.ts @@ -0,0 +1,113 @@ +import { BootModuleInfo, RenderToStringFunc, RenderToStringCallback } from '../npm/aspnet-prerendering/src/PrerenderingInterfaces'; +import * as path from 'path'; +declare var __non_webpack_require__; + +// Separate declaration and export just to add type checking on function signature +export const renderToString: RenderToStringFunc = renderToStringImpl; + +// This function is invoked by .NET code (via NodeServices). Its job is to hand off execution to the application's +// prerendering boot function. It can operate in two modes: +// [1] Legacy mode +// This is for backward compatibility with projects created with templates older than the generator version 0.6.0. +// In this mode, we don't really do anything here - we just load the 'aspnet-prerendering' NPM module (which must +// exist in node_modules, and must be v1.x (not v2+)), and pass through all the parameters to it. Code in +// 'aspnet-prerendering' v1.x will locate the boot function and invoke it. +// The drawback to this mode is that, for it to work, you have to deploy node_modules to production. +// [2] Current mode +// This is for projects created with the Yeoman generator 0.6.0+ (or projects manually updated). In this mode, +// we don't invoke 'require' at runtime at all. All our dependencies are bundled into the NuGet package, so you +// don't have to deploy node_modules to production. +// To determine whether we're in mode [1] or [2], the code locates your prerendering boot function, and checks whether +// a certain flag is attached to the function instance. +function renderToStringImpl(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any, overrideTimeoutMilliseconds: number) { + try { + const forceLegacy = isLegacyAspNetPrerendering(); + const renderToStringFunc = !forceLegacy && findRenderToStringFunc(applicationBasePath, bootModule); + const isNotLegacyMode = renderToStringFunc && renderToStringFunc['isServerRenderer']; + + if (isNotLegacyMode) { + // Current (non-legacy) mode - we invoke the exported function directly (instead of going through aspnet-prerendering) + // It's type-safe to just apply the incoming args to this function, because we already type-checked that it's a RenderToStringFunc, + // just like renderToStringImpl itself is. + renderToStringFunc.apply(null, arguments); + } else { + // Legacy mode - just hand off execution to 'aspnet-prerendering' v1.x, which must exist in node_modules at runtime + const aspNetPrerenderingV1RenderToString = require('aspnet-prerendering').renderToString; + if (aspNetPrerenderingV1RenderToString) { + aspNetPrerenderingV1RenderToString(callback, applicationBasePath, bootModule, absoluteRequestUrl, requestPathAndQuery, customDataParameter, overrideTimeoutMilliseconds); + } else { + callback('If you use aspnet-prerendering >= 2.0.0, you must update your server-side boot module to call createServerRenderer. ' + + 'Either update your boot module code, or revert to aspnet-prerendering version 1.x'); + } + } + } catch (ex) { + // Make sure loading errors are reported back to the .NET part of the app + callback( + 'Prerendering failed because of error: ' + + ex.stack + + '\nCurrent directory is: ' + + process.cwd() + ); + } +}; + +function findBootModule(applicationBasePath: string, bootModule: BootModuleInfo): any { + const bootModuleNameFullPath = path.resolve(applicationBasePath, bootModule.moduleName); + if (bootModule.webpackConfig) { + // If you're using asp-prerender-webpack-config, you're definitely in legacy mode + return null; + } else { + return __non_webpack_require__(bootModuleNameFullPath); + } +} + +function findRenderToStringFunc(applicationBasePath: string, bootModule: BootModuleInfo): RenderToStringFunc { + // First try to load the module + const foundBootModule = findBootModule(applicationBasePath, bootModule); + if (foundBootModule === null) { + return null; // Must be legacy mode + } + + // Now try to pick out the function they want us to invoke + let renderToStringFunc: RenderToStringFunc; + if (bootModule.exportName) { + // Explicitly-named export + renderToStringFunc = foundBootModule[bootModule.exportName]; + } else if (typeof foundBootModule !== 'function') { + // TypeScript-style default export + renderToStringFunc = foundBootModule.default; + } else { + // Native default export + renderToStringFunc = foundBootModule; + } + + // Validate the result + if (typeof renderToStringFunc !== 'function') { + if (bootModule.exportName) { + throw new Error(`The module at ${ bootModule.moduleName } has no function export named ${ bootModule.exportName }.`); + } else { + throw new Error(`The module at ${ bootModule.moduleName } does not export a default function, and you have not specified which export to invoke.`); + } + } + + return renderToStringFunc; +} + +function isLegacyAspNetPrerendering() { + const version = getAspNetPrerenderingPackageVersion(); + return version && /^1\./.test(version); +} + +function getAspNetPrerenderingPackageVersion() { + try { + const packageEntryPoint = __non_webpack_require__.resolve('aspnet-prerendering'); + const packageDir = path.dirname(packageEntryPoint); + const packageJsonPath = path.join(packageDir, 'package.json'); + const packageJson = __non_webpack_require__(packageJsonPath); + return packageJson.version.toString(); + } catch(ex) { + // Implies aspnet-prerendering isn't in node_modules at all (or node_modules itself doesn't exist, + // which will be the case in production based on latest templates). + return null; + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/WebpackDevMiddleware.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/WebpackDevMiddleware.ts new file mode 100644 index 0000000000..142ca000ab --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/WebpackDevMiddleware.ts @@ -0,0 +1,20 @@ +// Pass through the invocation to the 'aspnet-webpack' package, verifying that it can be loaded +export function createWebpackDevServer(callback) { + let aspNetWebpack; + try { + aspNetWebpack = require('aspnet-webpack'); + } catch (ex) { + // Developers sometimes have trouble with badly-configured Node installations, where it's unable + // to find node_modules. Or they accidentally fail to deploy node_modules, or even to run 'npm install'. + // Make sure such errors are reported back to the .NET part of the app. + callback( + 'Webpack dev middleware failed because of an error while loading \'aspnet-webpack\'. Error was: ' + + ex.stack + + '\nCurrent directory is: ' + + process.cwd() + ); + return; + } + + return aspNetWebpack.createWebpackDevServer.apply(this, arguments); +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/tsconfig.json new file mode 100644 index 0000000000..433cde0493 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/TypeScript/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es3", + "module": "commonjs", + "moduleResolution": "node", + "types": ["node"], + "lib": ["es2015"] + }, + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs new file mode 100644 index 0000000000..292f62d0fb --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs @@ -0,0 +1,123 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.SpaServices.Webpack +{ + /// + /// Based on https://github.com/aspnet/Proxy/blob/dev/src/Microsoft.AspNetCore.Proxy/ProxyMiddleware.cs + /// Differs in that, if the proxied request returns a 404, we pass through to the next middleware in the chain + /// This is useful for Webpack middleware, because it lets you fall back on prebuilt files on disk for + /// chunks not exposed by the current Webpack config (e.g., DLL/vendor chunks). + /// + internal class ConditionalProxyMiddleware + { + private const int DefaultHttpBufferSize = 4096; + + private readonly HttpClient _httpClient; + private readonly RequestDelegate _next; + private readonly ConditionalProxyMiddlewareOptions _options; + private readonly string _pathPrefix; + private readonly bool _pathPrefixIsRoot; + + public ConditionalProxyMiddleware( + RequestDelegate next, + string pathPrefix, + ConditionalProxyMiddlewareOptions options) + { + if (!pathPrefix.StartsWith("/")) + { + pathPrefix = "/" + pathPrefix; + } + + _next = next; + _pathPrefix = pathPrefix; + _pathPrefixIsRoot = string.Equals(_pathPrefix, "/", StringComparison.Ordinal); + _options = options; + _httpClient = new HttpClient(new HttpClientHandler()); + _httpClient.Timeout = _options.RequestTimeout; + } + + public async Task Invoke(HttpContext context) + { + if (context.Request.Path.StartsWithSegments(_pathPrefix) || _pathPrefixIsRoot) + { + var didProxyRequest = await PerformProxyRequest(context); + if (didProxyRequest) + { + return; + } + } + + // Not a request we can proxy + await _next.Invoke(context); + } + + private async Task PerformProxyRequest(HttpContext context) + { + var requestMessage = new HttpRequestMessage(); + + // Copy the request headers + foreach (var header in context.Request.Headers) + { + if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray())) + { + requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + } + + requestMessage.Headers.Host = _options.Host + ":" + _options.Port; + var uriString = + $"{_options.Scheme}://{_options.Host}:{_options.Port}{context.Request.Path}{context.Request.QueryString}"; + requestMessage.RequestUri = new Uri(uriString); + requestMessage.Method = new HttpMethod(context.Request.Method); + + using ( + var responseMessage = await _httpClient.SendAsync( + requestMessage, + HttpCompletionOption.ResponseHeadersRead, + context.RequestAborted)) + { + if (responseMessage.StatusCode == HttpStatusCode.NotFound) + { + // Let some other middleware handle this + return false; + } + + // We can handle this + context.Response.StatusCode = (int) responseMessage.StatusCode; + foreach (var header in responseMessage.Headers) + { + context.Response.Headers[header.Key] = header.Value.ToArray(); + } + + foreach (var header in responseMessage.Content.Headers) + { + context.Response.Headers[header.Key] = header.Value.ToArray(); + } + + // SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response. + context.Response.Headers.Remove("transfer-encoding"); + + using (var responseStream = await responseMessage.Content.ReadAsStreamAsync()) + { + try + { + await responseStream.CopyToAsync(context.Response.Body, DefaultHttpBufferSize, context.RequestAborted); + } + catch (OperationCanceledException) + { + // The CopyToAsync task will be canceled if the client disconnects (e.g., user + // closes or refreshes the browser tab). Don't treat this as an error. + } + } + + return true; + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddlewareOptions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddlewareOptions.cs new file mode 100644 index 0000000000..2c3311aabd --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddlewareOptions.cs @@ -0,0 +1,20 @@ +using System; + +namespace Microsoft.AspNetCore.SpaServices.Webpack +{ + internal class ConditionalProxyMiddlewareOptions + { + public ConditionalProxyMiddlewareOptions(string scheme, string host, string port, TimeSpan requestTimeout) + { + Scheme = scheme; + Host = host; + Port = port; + RequestTimeout = requestTimeout; + } + + public string Scheme { get; } + public string Host { get; } + public string Port { get; } + public TimeSpan RequestTimeout { get; } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs new file mode 100644 index 0000000000..2e8f92ea3c --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs @@ -0,0 +1,148 @@ +using System; +using System.IO; +using System.Threading; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.SpaServices.Webpack; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Extension methods that can be used to enable Webpack dev middleware support. + /// + public static class WebpackDevMiddleware + { + private const string DefaultConfigFile = "webpack.config.js"; + + private static readonly JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings + { + // Note that the aspnet-webpack JS code specifically expects options to be serialized with + // PascalCase property names, so it's important to be explicit about this contract resolver + ContractResolver = new DefaultContractResolver(), + + TypeNameHandling = TypeNameHandling.None + }; + + /// + /// Enables Webpack dev middleware support. This hosts an instance of the Webpack compiler in memory + /// in your application so that you can always serve up-to-date Webpack-built resources without having + /// to run the compiler manually. Since the Webpack compiler instance is retained in memory, incremental + /// compilation is vastly faster that re-running the compiler from scratch. + /// + /// Incoming requests that match Webpack-built files will be handled by returning the Webpack compiler + /// output directly, regardless of files on disk. If compilation is in progress when the request arrives, + /// the response will pause until updated compiler output is ready. + /// + /// The . + /// Options for configuring the Webpack compiler instance. + public static void UseWebpackDevMiddleware( + this IApplicationBuilder appBuilder, + WebpackDevMiddlewareOptions options = null) + { + // Prepare options + if (options == null) + { + options = new WebpackDevMiddlewareOptions(); + } + + // Validate options + if (options.ReactHotModuleReplacement && !options.HotModuleReplacement) + { + throw new ArgumentException( + "To enable ReactHotModuleReplacement, you must also enable HotModuleReplacement."); + } + + // Unlike other consumers of NodeServices, WebpackDevMiddleware dosen't share Node instances, nor does it + // use your DI configuration. It's important for WebpackDevMiddleware to have its own private Node instance + // because it must *not* restart when files change (if it did, you'd lose all the benefits of Webpack + // middleware). And since this is a dev-time-only feature, it doesn't matter if the default transport isn't + // as fast as some theoretical future alternative. + var nodeServicesOptions = new NodeServicesOptions(appBuilder.ApplicationServices); + nodeServicesOptions.WatchFileExtensions = new string[] { }; // Don't watch anything + if (!string.IsNullOrEmpty(options.ProjectPath)) + { + nodeServicesOptions.ProjectPath = options.ProjectPath; + } + + if (options.EnvironmentVariables != null) + { + foreach (var kvp in options.EnvironmentVariables) + { + nodeServicesOptions.EnvironmentVariables[kvp.Key] = kvp.Value; + } + } + + var nodeServices = NodeServicesFactory.CreateNodeServices(nodeServicesOptions); + + // Get a filename matching the middleware Node script + var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware), + "/Content/Node/webpack-dev-middleware.js"); + var nodeScript = new StringAsTempFile(script, nodeServicesOptions.ApplicationStoppingToken); // Will be cleaned up on process exit + + // Ideally, this would be relative to the application's PathBase (so it could work in virtual directories) + // but it's not clear that such information exists during application startup, as opposed to within the context + // of a request. + var hmrEndpoint = !string.IsNullOrEmpty(options.HotModuleReplacementEndpoint) + ? options.HotModuleReplacementEndpoint + : "/__webpack_hmr"; // Matches webpack's built-in default + + // Tell Node to start the server hosting webpack-dev-middleware + var devServerOptions = new + { + webpackConfigPath = Path.Combine(nodeServicesOptions.ProjectPath, options.ConfigFile ?? DefaultConfigFile), + suppliedOptions = options, + understandsMultiplePublicPaths = true, + hotModuleReplacementEndpointUrl = hmrEndpoint + }; + var devServerInfo = + nodeServices.InvokeExportAsync(nodeScript.FileName, "createWebpackDevServer", + JsonConvert.SerializeObject(devServerOptions, jsonSerializerSettings)).Result; + + // If we're talking to an older version of aspnet-webpack, it will return only a single PublicPath, + // not an array of PublicPaths. Handle that scenario. + if (devServerInfo.PublicPaths == null) + { + devServerInfo.PublicPaths = new[] { devServerInfo.PublicPath }; + } + + // Proxy the corresponding requests through ASP.NET and into the Node listener + // Anything under / (e.g., /dist) is proxied as a normal HTTP request with a typical timeout (100s is the default from HttpClient), + // plus /__webpack_hmr is proxied with infinite timeout, because it's an EventSource (long-lived request). + foreach (var publicPath in devServerInfo.PublicPaths) + { + appBuilder.UseProxyToLocalWebpackDevMiddleware(publicPath + hmrEndpoint, devServerInfo.Port, Timeout.InfiniteTimeSpan); + appBuilder.UseProxyToLocalWebpackDevMiddleware(publicPath, devServerInfo.Port, TimeSpan.FromSeconds(100)); + } + } + + private static void UseProxyToLocalWebpackDevMiddleware(this IApplicationBuilder appBuilder, string publicPath, int proxyToPort, TimeSpan requestTimeout) + { + // Note that this is hardcoded to make requests to "localhost" regardless of the hostname of the + // server as far as the client is concerned. This is because ConditionalProxyMiddlewareOptions is + // the one making the internal HTTP requests, and it's going to be to some port on this machine + // because aspnet-webpack hosts the dev server there. We can't use the hostname that the client + // sees, because that could be anything (e.g., some upstream load balancer) and we might not be + // able to make outbound requests to it from here. + // Also note that the webpack HMR service always uses HTTP, even if your app server uses HTTPS, + // because the HMR service has no need for HTTPS (the client doesn't see it directly - all traffic + // to it is proxied), and the HMR service couldn't use HTTPS anyway (in general it wouldn't have + // the necessary certificate). + var proxyOptions = new ConditionalProxyMiddlewareOptions( + "http", "localhost", proxyToPort.ToString(), requestTimeout); + appBuilder.UseMiddleware(publicPath, proxyOptions); + } + +#pragma warning disable CS0649 + class WebpackDevServerInfo + { + public int Port { get; set; } + public string[] PublicPaths { get; set; } + + // For back-compatibility with older versions of aspnet-webpack, in the case where your webpack + // configuration contains exactly one config entry. This will be removed soon. + public string PublicPath { get; set; } + } + } +#pragma warning restore CS0649 +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddlewareOptions.cs b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddlewareOptions.cs new file mode 100644 index 0000000000..df50100dc7 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddlewareOptions.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.SpaServices.Webpack +{ + /// + /// Options for configuring a Webpack dev middleware compiler. + /// + public class WebpackDevMiddlewareOptions + { + /// + /// If true, hot module replacement (HMR) will be enabled. This automatically updates Webpack-built + /// resources (such as JavaScript, CSS, or images) in your web browser whenever source files are changed. + /// + public bool HotModuleReplacement { get; set; } + + /// + /// If set, overrides the URL that Webpack's client-side code will connect to when listening for updates. + /// This must be a root-relative URL similar to "/__webpack_hmr" (which is the default endpoint). + /// + public string HotModuleReplacementEndpoint { get; set; } + + /// + /// Overrides the internal port number that client-side HMR code will connect to. + /// + public int HotModuleReplacementServerPort { get; set; } + + /// + /// If true, enables React-specific extensions to Webpack's hot module replacement (HMR) feature. + /// This enables React components to be updated without losing their in-memory state. + /// + public bool ReactHotModuleReplacement { get; set; } + + /// + /// Specifies additional options to be passed to the Webpack Hot Middleware client, if used. + /// + public IDictionary HotModuleReplacementClientOptions { get; set; } + + /// + /// Specifies the Webpack configuration file to be used. If not set, defaults to 'webpack.config.js'. + /// + public string ConfigFile { get; set; } + + /// + /// The root path of your project. Webpack runs in this context. + /// + public string ProjectPath { get; set; } + + /// + /// Specifies additional environment variables to be passed to the Node instance hosting + /// the webpack compiler. + /// + public IDictionary EnvironmentVariables { get; set; } + + /// + /// Specifies a value for the "env" parameter to be passed into the Webpack configuration + /// function. The value must be JSON-serializable, and will only be used if the Webpack + /// configuration is exported as a function. + /// + public object EnvParam { get; set; } + } +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/baseline.netcore.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/baseline.netcore.json new file mode 100644 index 0000000000..d6301ef42b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/baseline.netcore.json @@ -0,0 +1,751 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.SpaServices, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.PrerenderingServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddSpaPrerenderer", + "Parameters": [ + { + "Name": "serviceCollection", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.SpaRouteExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "MapSpaFallbackRoute", + "Parameters": [ + { + "Name": "routeBuilder", + "Type": "Microsoft.AspNetCore.Routing.IRouteBuilder" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "defaults", + "Type": "System.Object" + }, + { + "Name": "constraints", + "Type": "System.Object", + "DefaultValue": "null" + }, + { + "Name": "dataTokens", + "Type": "System.Object", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapSpaFallbackRoute", + "Parameters": [ + { + "Name": "routeBuilder", + "Type": "Microsoft.AspNetCore.Routing.IRouteBuilder" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "templatePrefix", + "Type": "System.String" + }, + { + "Name": "defaults", + "Type": "System.Object" + }, + { + "Name": "constraints", + "Type": "System.Object", + "DefaultValue": "null" + }, + { + "Name": "dataTokens", + "Type": "System.Object", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.WebpackDevMiddleware", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseWebpackDevMiddleware", + "Parameters": [ + { + "Name": "appBuilder", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.SpaServices.Webpack.WebpackDevMiddlewareOptions", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.Webpack.WebpackDevMiddlewareOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_HotModuleReplacement", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_HotModuleReplacement", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_HotModuleReplacementEndpoint", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_HotModuleReplacementEndpoint", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_HotModuleReplacementServerPort", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_HotModuleReplacementServerPort", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ReactHotModuleReplacement", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ReactHotModuleReplacement", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_HotModuleReplacementClientOptions", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IDictionary", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_HotModuleReplacementClientOptions", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.IDictionary" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ConfigFile", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ConfigFile", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProjectPath", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProjectPath", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_EnvironmentVariables", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IDictionary", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_EnvironmentVariables", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.IDictionary" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_EnvParam", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_EnvParam", + "Parameters": [ + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerenderer", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "RenderToString", + "Parameters": [ + { + "Name": "moduleName", + "Type": "System.String" + }, + { + "Name": "exportName", + "Type": "System.String", + "DefaultValue": "null" + }, + { + "Name": "customDataParameter", + "Type": "System.Object", + "DefaultValue": "null" + }, + { + "Name": "timeoutMilliseconds", + "Type": "System.Int32", + "DefaultValue": "0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.Prerendering.JavaScriptModuleExport", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ModuleName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExportName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ExportName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "moduleName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.Prerendering.Prerenderer", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "RenderToString", + "Parameters": [ + { + "Name": "applicationBasePath", + "Type": "System.String" + }, + { + "Name": "nodeServices", + "Type": "Microsoft.AspNetCore.NodeServices.INodeServices" + }, + { + "Name": "applicationStoppingToken", + "Type": "System.Threading.CancellationToken" + }, + { + "Name": "bootModule", + "Type": "Microsoft.AspNetCore.SpaServices.Prerendering.JavaScriptModuleExport" + }, + { + "Name": "requestAbsoluteUrl", + "Type": "System.String" + }, + { + "Name": "requestPathAndQuery", + "Type": "System.String" + }, + { + "Name": "customDataParameter", + "Type": "System.Object" + }, + { + "Name": "timeoutMilliseconds", + "Type": "System.Int32" + }, + { + "Name": "requestPathBase", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.Prerendering.PrerenderTagHelper", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Razor.TagHelpers.TagHelper", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ProcessAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext" + }, + { + "Name": "output", + "Type": "Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Razor.TagHelpers.ITagHelperComponent", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ModuleName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ModuleName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExportName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ExportName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_CustomDataParameter", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CustomDataParameter", + "Parameters": [ + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TimeoutMillisecondsParameter", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TimeoutMillisecondsParameter", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ViewContext", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Mvc.Rendering.ViewContext", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ViewContext", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Mvc.Rendering.ViewContext" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "serviceProvider", + "Type": "System.IServiceProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.SpaServices.Prerendering.RenderToStringResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Globals", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Linq.JObject", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Globals", + "Parameters": [ + { + "Name": "value", + "Type": "Newtonsoft.Json.Linq.JObject" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Html", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Html", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RedirectUrl", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RedirectUrl", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_StatusCode", + "Parameters": [], + "ReturnType": "System.Nullable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_StatusCode", + "Parameters": [ + { + "Name": "value", + "Type": "System.Nullable" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateGlobalsAssignmentScript", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore new file mode 100644 index 0000000000..93666fdf9a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore @@ -0,0 +1,5 @@ +/node_modules/ +**/*.js +**/*.d.ts +**/*.metadata.json +/compiled diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.npmignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.npmignore new file mode 100644 index 0000000000..2df2c09c07 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.npmignore @@ -0,0 +1,3 @@ +!/*.js +!/*.d.ts +/compiled diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/LICENSE.txt b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/LICENSE.txt new file mode 100644 index 0000000000..0bdc1962b6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/package.json new file mode 100644 index 0000000000..5153adeaad --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/package.json @@ -0,0 +1,33 @@ +{ + "name": "aspnet-angular", + "version": "0.1.1", + "description": "Helpers for using Angular in ASP.NET Core projects", + "main": "index.js", + "scripts": { + "prepublish": "rimraf *.d.ts && ngc && echo 'Finished building NPM package \"aspnet-angular\"'", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/aspnet/JavaScriptServices.git" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aspnet/JavaScriptServices/issues" + }, + "devDependencies": { + "@angular/common": "^4.3.2", + "@angular/compiler": "^4.3.2", + "@angular/compiler-cli": "^4.3.2", + "@angular/core": "^4.3.2", + "@angular/http": "^4.3.2", + "@angular/platform-browser": "^4.3.2", + "rimraf": "^2.6.1", + "rxjs": "^5.4.2", + "zone.js": "^0.8.16" + }, + "peerDependencies": { + "@angular/core": "^4.2.5 || ^5.0.0-beta" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/HttpWithStateTransfer.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/HttpWithStateTransfer.ts new file mode 100644 index 0000000000..517dd7e784 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/HttpWithStateTransfer.ts @@ -0,0 +1,94 @@ +import { Provider, NgModule, Inject } from '@angular/core'; +import { Headers, Http, ResponseOptions, RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/observable/of'; +import 'rxjs/add/operator/map'; +const globalSerializedStateKey = 'HTTP_STATE_TRANSFER'; +const backingStoreDIToken = 'HTTP_STATE_BACKING_STORE'; + +export interface CacheOptions { + permanent: boolean; +} + +export interface CachedHttpResponse { + headers: { [name: string]: any } | null; + status: number; + statusText: string | null; + text: string; + url: string; +} + +export type BackingStore = { [key: string]: CachedHttpResponse }; + +export class HttpWithStateTransfer { + private backingStore: BackingStore; + private http: Http; + + constructor(@Inject(Http) http: Http, @Inject(backingStoreDIToken) backingStore: BackingStore) { + this.http = http; + this.backingStore = backingStore; + } + + public stateForTransfer(): any { + return { [globalSerializedStateKey]: this.backingStore }; + } + + public get(url: string, options?: CacheOptions, requestOptions?: RequestOptionsArgs): Observable { + return this.getCachedResponse(/* cacheKey */ url, () => this.http.get(url, requestOptions), options); + } + + private getCachedResponse(cacheKey: string, provider: () => Observable, options?: CacheOptions): Observable { + // By default, the cache is only used for the *first* client-side read. So, we're only performing + // a one-time transfer of server-side response to the client. If you want to keep and reuse cached + // responses continually during server-side and client-side execution, set 'permanent' to 'true. + const isClient = typeof window !== 'undefined'; + const isPermanent = options && options.permanent; + + const allowReadFromCache = isClient || isPermanent; + if (allowReadFromCache && this.backingStore.hasOwnProperty(cacheKey)) { + const cachedValue = this.backingStore[cacheKey]; + if (!isPermanent) { + delete this.backingStore[cacheKey]; + } + return Observable.of(new Response(new ResponseOptions({ + body: cachedValue.text, + headers: new Headers(cachedValue.headers), + status: cachedValue.status, + url: cachedValue.url + }))); + } + + return provider() + .map(response => { + const allowWriteToCache = !isClient || isPermanent; + if (allowWriteToCache) { + this.backingStore[cacheKey] = { + headers: response.headers ? response.headers.toJSON() : null, + status: response.status, + statusText: response.statusText, + text: response.text(), + url: response.url + }; + } + + return response; + }); + } +} + +export function defaultBackingStoreFactory() { + const transferredData = typeof window !== 'undefined' ? (window as any)[globalSerializedStateKey] : null; + return transferredData || {}; +} + +@NgModule({ + providers: [ + // The backing store is a separate DI service so you could override exactly how it gets + // transferred from server to client + { provide: backingStoreDIToken, useFactory: defaultBackingStoreFactory }, + + { provide: HttpWithStateTransfer, useClass: HttpWithStateTransfer }, + ] +}) +export class HttpWithStateTransferModule { +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/index.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/index.ts new file mode 100644 index 0000000000..93d21853b3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/index.ts @@ -0,0 +1 @@ +export * from './HttpWithStateTransfer'; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/tsconfig.json new file mode 100644 index 0000000000..3c6a30b769 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "moduleResolution": "node", + "module": "commonjs", + "target": "es5", + "declaration": true, + "outDir": ".", + "lib": ["es2015", "dom"] + }, + "files": [ + "src/index.ts" + ], + "exclude": [ + "node_modules" + ], + "angularCompilerOptions": { + "genDir": "compiled" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/.gitignore new file mode 100644 index 0000000000..e1ef4d0a83 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/.gitignore @@ -0,0 +1,8 @@ +/typings/ +/node_modules/ +/**/*.js + +/**/.d.ts +!/src/**/*.d.ts + +yarn.lock diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/.npmignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/.npmignore new file mode 100644 index 0000000000..542947e4a9 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/.npmignore @@ -0,0 +1,4 @@ +!/*.js +!/*.d.ts +/typings/ +yarn.lock diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/LICENSE.txt b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/LICENSE.txt new file mode 100644 index 0000000000..0bdc1962b6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/README.md b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/README.md new file mode 100644 index 0000000000..30cdc0c29f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/README.md @@ -0,0 +1,6 @@ +# Not for general use + +This NPM package is an internal implementation detail of the `Microsoft.AspNetCore.SpaServices` NuGet package. + +You should not use this package directly in your own applications, because it is not supported, and there are no +guarantees about how its APIs will change in the future. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json new file mode 100644 index 0000000000..e239460fe8 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json @@ -0,0 +1,27 @@ +{ + "name": "aspnet-prerendering", + "version": "3.0.1", + "description": "Helpers for server-side rendering of JavaScript applications in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.", + "main": "index.js", + "scripts": { + "prepublish": "rimraf *.d.ts && tsc && echo 'Finished building NPM package \"aspnet-prerendering\"'", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aspnet/JavaScriptServices/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/aspnet/JavaScriptServices.git" + }, + "dependencies": { + "domain-task": "^3.0.0" + }, + "devDependencies": { + "@types/node": "^6.0.42", + "rimraf": "^2.5.4", + "typescript": "^2.2.1" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts new file mode 100644 index 0000000000..a313cbade0 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts @@ -0,0 +1,112 @@ +import * as url from 'url'; +import * as path from 'path'; +import * as domain from 'domain'; +import { run as domainTaskRun, baseUrl as domainTaskBaseUrl } from 'domain-task/main'; +import { BootFunc, BootFuncParams, BootModuleInfo, RenderToStringCallback, RenderToStringFunc } from './PrerenderingInterfaces'; + +const defaultTimeoutMilliseconds = 30 * 1000; + +export function createServerRenderer(bootFunc: BootFunc): RenderToStringFunc { + const resultFunc = (callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any, overrideTimeoutMilliseconds: number, requestPathBase: string) => { + // Prepare a promise that will represent the completion of all domain tasks in this execution context. + // The boot code will wait for this before performing its final render. + let domainTaskCompletionPromiseResolve; + const domainTaskCompletionPromise = new Promise((resolve, reject) => { + domainTaskCompletionPromiseResolve = resolve; + }); + const parsedAbsoluteRequestUrl = url.parse(absoluteRequestUrl); + const params: BootFuncParams = { + // It's helpful for boot funcs to receive the query as a key-value object, so parse it here + // e.g., react-redux-router requires location.query to be a key-value object for consistency with client-side behaviour + location: url.parse(requestPathAndQuery, /* parseQueryString */ true), + origin: parsedAbsoluteRequestUrl.protocol + '//' + parsedAbsoluteRequestUrl.host, + url: requestPathAndQuery, + baseUrl: (requestPathBase || '') + '/', + absoluteUrl: absoluteRequestUrl, + domainTasks: domainTaskCompletionPromise, + data: customDataParameter + }; + const absoluteBaseUrl = params.origin + params.baseUrl; // Should be same value as page's + + // Open a new domain that can track all the async tasks involved in the app's execution + domainTaskRun(/* code to run */ () => { + // Workaround for Node bug where native Promise continuations lose their domain context + // (https://github.com/nodejs/node-v0.x-archive/issues/8648) + // The domain.active property is set by the domain-context module + bindPromiseContinuationsToDomain(domainTaskCompletionPromise, domain['active']); + + // Make the base URL available to the 'domain-tasks/fetch' helper within this execution context + domainTaskBaseUrl(absoluteBaseUrl); + + // Begin rendering, and apply a timeout + const bootFuncPromise = bootFunc(params); + if (!bootFuncPromise || typeof bootFuncPromise.then !== 'function') { + callback(`Prerendering failed because the boot function in ${bootModule.moduleName} did not return a promise.`, null); + return; + } + const timeoutMilliseconds = overrideTimeoutMilliseconds || defaultTimeoutMilliseconds; // e.g., pass -1 to override as 'never time out' + const bootFuncPromiseWithTimeout = timeoutMilliseconds > 0 + ? wrapWithTimeout(bootFuncPromise, timeoutMilliseconds, + `Prerendering timed out after ${timeoutMilliseconds}ms because the boot function in '${bootModule.moduleName}' ` + + 'returned a promise that did not resolve or reject. Make sure that your boot function always resolves or ' + + 'rejects its promise. You can change the timeout value using the \'asp-prerender-timeout\' tag helper.') + : bootFuncPromise; + + // Actually perform the rendering + bootFuncPromiseWithTimeout.then(successResult => { + callback(null, successResult); + }, error => { + callback(error, null); + }); + }, /* completion callback */ errorOrNothing => { + if (errorOrNothing) { + callback(errorOrNothing, null); + } else { + // There are no more ongoing domain tasks (typically data access operations), so we can resolve + // the domain tasks promise which notifies the boot code that it can do its final render. + domainTaskCompletionPromiseResolve(); + } + }); + }; + + // Indicate to the prerendering code bundled into Microsoft.AspNetCore.SpaServices that this is a serverside rendering + // function, so it can be invoked directly. This flag exists only so that, in its absence, we can run some different + // backward-compatibility logic. + resultFunc['isServerRenderer'] = true; + + return resultFunc; +} + +function wrapWithTimeout(promise: Promise, timeoutMilliseconds: number, timeoutRejectionValue: any): Promise { + return new Promise((resolve, reject) => { + const timeoutTimer = setTimeout(() => { + reject(timeoutRejectionValue); + }, timeoutMilliseconds); + + promise.then( + resolvedValue => { + clearTimeout(timeoutTimer); + resolve(resolvedValue); + }, + rejectedValue => { + clearTimeout(timeoutTimer); + reject(rejectedValue); + } + ) + }); +} + +function bindPromiseContinuationsToDomain(promise: Promise, domainInstance: domain.Domain) { + const originalThen = promise.then; + promise.then = (function then(resolve, reject) { + if (typeof resolve === 'function') { + resolve = domainInstance.bind(resolve); + } + + if (typeof reject === 'function') { + reject = domainInstance.bind(reject); + } + + return originalThen.call(this, resolve, reject); + }) as any; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/PrerenderingInterfaces.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/PrerenderingInterfaces.ts new file mode 100644 index 0000000000..ae101bdff3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/PrerenderingInterfaces.ts @@ -0,0 +1,39 @@ +export interface RenderToStringFunc { + (callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any, overrideTimeoutMilliseconds: number, requestPathBase: string): void; +} + +export interface RenderToStringCallback { + (error: any, result?: RenderResult): void; +} + +export interface RenderToStringResult { + html: string; + statusCode?: number; + globals?: { [key: string]: any }; +} + +export interface RedirectResult { + redirectUrl: string; +} + +export type RenderResult = RenderToStringResult | RedirectResult; + +export interface BootFunc { + (params: BootFuncParams): Promise; +} + +export interface BootFuncParams { + location: any; // e.g., Location object containing information '/some/path' + origin: string; // e.g., 'https://example.com:1234' + url: string; // e.g., '/some/path' + baseUrl: string; // e.g., '' or '/myVirtualDir' + absoluteUrl: string; // e.g., 'https://example.com:1234/some/path' + domainTasks: Promise; + data: any; // any custom object passed through from .NET +} + +export interface BootModuleInfo { + moduleName: string; + exportName?: string; + webpackConfig?: string; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/index.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/index.ts new file mode 100644 index 0000000000..082f07c785 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/index.ts @@ -0,0 +1,2 @@ +export * from './Prerendering'; +export * from './PrerenderingInterfaces'; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/tsconfig.json new file mode 100644 index 0000000000..19facdd357 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "commonjs", + "target": "es5", + "declaration": true, + "outDir": ".", + "lib": ["es2015", "dom"] + }, + "files": [ + "src/index.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/.gitignore new file mode 100644 index 0000000000..025755a469 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/.gitignore @@ -0,0 +1,3 @@ +/node_modules/ +/*.js +/*.d.ts diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/.npmignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/.npmignore new file mode 100644 index 0000000000..858cdc4c3b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/.npmignore @@ -0,0 +1,3 @@ +!/*.js +!/*.d.ts +/typings/ diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/LICENSE.txt b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/LICENSE.txt new file mode 100644 index 0000000000..0bdc1962b6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/README.md b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/README.md new file mode 100644 index 0000000000..65f78bc1b6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/README.md @@ -0,0 +1,11 @@ +# Not for general use + +This NPM package is an internal implementation detail of the `Microsoft.AspNetCore.SpaServices` NuGet package. + +You should not use this package directly in your own applications, because it is not supported, and there are no +guarantees about how its APIs will change in the future. + +## History + +* Version 1.x amends the Webpack config to insert `react-transform` and `react-transform-hmr` entries on `babel-loader`. +* Version 2.x drops support for the Babel plugin, and instead amends the Webpack config to insert `react-hot-loader/webpack` and `react-hot-loader/patch` entries. This means it works with React Hot Loader v3. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/package.json new file mode 100644 index 0000000000..19a2a1b70e --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/package.json @@ -0,0 +1,28 @@ +{ + "name": "aspnet-webpack-react", + "version": "3.0.0", + "description": "Helpers for using Webpack with React in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.", + "main": "index.js", + "scripts": { + "prepublish": "rimraf *.d.ts && tsc && echo 'Finished building NPM package \"aspnet-webpack-react\"'", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aspnet/JavaScriptServices/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/aspnet/JavaScriptServices.git" + }, + "devDependencies": { + "@types/webpack": "^2.2.0", + "rimraf": "^2.5.4", + "typescript": "^2.0.0", + "webpack": "^2.2.0" + }, + "peerDependencies": { + "webpack": "^2.2.0" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/HotModuleReplacement.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/HotModuleReplacement.ts new file mode 100644 index 0000000000..ca1bad63ad --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/HotModuleReplacement.ts @@ -0,0 +1,66 @@ +import * as webpack from 'webpack'; + +const reactHotLoaderWebpackLoader = 'react-hot-loader/webpack'; +const reactHotLoaderPatch = 'react-hot-loader/patch'; +const supportedTypeScriptLoaders = ['ts-loader', 'awesome-typescript-loader']; + +export function addReactHotModuleReplacementConfig(webpackConfig: webpack.Configuration) { + const moduleConfig = webpackConfig.module as webpack.NewModule; + const moduleRules = moduleConfig.rules; + if (!moduleRules) { + return; // Unknown rules list format. Might be Webpack 1.x, which is not supported. + } + + // Find the rule that loads TypeScript files, and prepend 'react-hot-loader/webpack' + // to its array of loaders + for (let ruleIndex = 0; ruleIndex < moduleRules.length; ruleIndex++) { + // We only support NewUseRule (i.e., { use: ... }) because OldUseRule doesn't accept array values + const rule = moduleRules[ruleIndex] as webpack.NewUseRule; + if (!rule.use) { + continue; + } + + // We're looking for the first 'use' value that's a TypeScript loader + const loadersArray = rule.use instanceof Array ? rule.use : [rule.use]; + const isTypescriptLoader = supportedTypeScriptLoaders.some(typeScriptLoaderName => containsLoader(loadersArray, typeScriptLoaderName)); + if (!isTypescriptLoader) { + continue; + } + + // This is the one - prefix it with the react-hot-loader loader + // (unless it's already in there somewhere) + if (!containsLoader(loadersArray, reactHotLoaderWebpackLoader)) { + loadersArray.unshift(reactHotLoaderWebpackLoader); + rule.use = loadersArray; // In case we normalised it to an array + } + break; + } + + // Ensure the entrypoint is prefixed with 'react-hot-loader/patch' (unless it's already in there). + // We only support entrypoints of the form { name: value } (not just 'name' or ['name']) + // because that gives us a place to prepend the new value + if (!webpackConfig.entry || typeof webpackConfig.entry === 'string' || webpackConfig.entry instanceof Array) { + throw new Error('Cannot enable React HMR because \'entry\' in Webpack config is not of the form { name: value }'); + } + const entryConfig = webpackConfig.entry as webpack.Entry; + Object.getOwnPropertyNames(entryConfig).forEach(entrypointName => { + if (typeof(entryConfig[entrypointName]) === 'string') { + // Normalise to array + entryConfig[entrypointName] = [entryConfig[entrypointName] as string]; + } + + let entryValueArray = entryConfig[entrypointName] as string[]; + if (entryValueArray.indexOf(reactHotLoaderPatch) < 0) { + entryValueArray.unshift(reactHotLoaderPatch); + } + }); +} + +function containsLoader(loadersArray: webpack.Loader[], loaderName: string) { + return loadersArray.some(loader => { + // Allow 'use' values to be either { loader: 'name' } or 'name' + // No need to support legacy webpack.OldLoader + const actualLoaderName = (loader as webpack.NewLoader).loader || (loader as string); + return actualLoaderName && new RegExp(`\\b${ loaderName }\\b`).test(actualLoaderName); + }); +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/index.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/index.ts new file mode 100644 index 0000000000..c4284f2ff6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/index.ts @@ -0,0 +1,6 @@ +export { addReactHotModuleReplacementConfig } from './HotModuleReplacement'; + +// Temporarily alias addReactHotModuleReplacementConfig as addReactHotModuleReplacementBabelTransform for backward +// compatibility with aspnet-webpack 1.x. In aspnet-webpack 2.0, we can drop the old name (and also deprecate +// some other no-longer-supported functionality, such as LoadViaWebpack). +export { addReactHotModuleReplacementConfig as addReactHotModuleReplacementBabelTransform } from './HotModuleReplacement'; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/tsconfig.json new file mode 100644 index 0000000000..2ebeb45d5f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "commonjs", + "target": "es5", + "declaration": true, + "outDir": ".", + "lib": ["es2015"] + }, + "files": [ + "src/index.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/.gitignore new file mode 100644 index 0000000000..025755a469 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/.gitignore @@ -0,0 +1,3 @@ +/node_modules/ +/*.js +/*.d.ts diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/.npmignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/.npmignore new file mode 100644 index 0000000000..858cdc4c3b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/.npmignore @@ -0,0 +1,3 @@ +!/*.js +!/*.d.ts +/typings/ diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/LICENSE.txt b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/LICENSE.txt new file mode 100644 index 0000000000..0bdc1962b6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/README.md b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/README.md new file mode 100644 index 0000000000..30cdc0c29f --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/README.md @@ -0,0 +1,6 @@ +# Not for general use + +This NPM package is an internal implementation detail of the `Microsoft.AspNetCore.SpaServices` NuGet package. + +You should not use this package directly in your own applications, because it is not supported, and there are no +guarantees about how its APIs will change in the future. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package-lock.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package-lock.json new file mode 100644 index 0000000000..0e453d1bb3 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package-lock.json @@ -0,0 +1,2875 @@ +{ + "name": "aspnet-webpack", + "version": "3.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/connect": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.31.tgz", + "integrity": "sha512-OPSxsP6XqA3984KWDUXq/u05Hu8VWa/2rUVlw/aDUOx87BptIep6xb3NdCxCpKLfLdjZcCE5jR+gouTul3gjdA==", + "dev": true, + "requires": { + "@types/node": "6.0.96" + } + }, + "@types/node": { + "version": "6.0.96", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.96.tgz", + "integrity": "sha512-fsOOY6tMQ3jCB2wD51XFDmmpgm4wVKkJECdcVRqapbJEa7awJDcr+SaH8toz+4r4KW8YQ3M7ybXMoSDo1QGewA==", + "dev": true + }, + "@types/tapable": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.2.tgz", + "integrity": "sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ==", + "dev": true + }, + "@types/uglify-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.2.tgz", + "integrity": "sha512-o8hU2+4xsyGC27Vujoklvxl88Ew5zmJuTBYMX1Uro2rYUt4HEFJKL6fuq8aGykvS+ssIsIzerWWP2DRxonownQ==", + "dev": true, + "requires": { + "source-map": "0.6.1" + } + }, + "@types/webpack": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.1.3.tgz", + "integrity": "sha512-NoGVTCumOsyFfuy3934f3ktiJi+wcXHJFxT47tby3iCpuo6M/WjFA9VqT5bYO+FE46i3R0N00RpJX75HxHKDaQ==", + "dev": true, + "requires": { + "@types/node": "6.0.96", + "@types/tapable": "1.0.2", + "@types/uglify-js": "3.0.2", + "source-map": "0.6.1" + } + }, + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "5.5.3" + } + }, + "ajv": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", + "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", + "dev": true, + "requires": { + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1", + "uri-js": "4.2.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "3.1.10", + "normalize-path": "2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "atob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "1.2.0", + "browserify-des": "1.0.1", + "evp_bytestokey": "1.0.3" + } + }, + "browserify-des": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz", + "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "2.0.3" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "randombytes": "2.0.6" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "elliptic": "6.4.0", + "inherits": "2.0.3", + "parse-asn1": "5.1.1" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "1.0.6" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "1.3.0", + "ieee754": "1.1.11", + "isarray": "1.0.0" + } + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "3.5.1", + "chownr": "1.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.3", + "mississippi": "2.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "5.3.0", + "unique-filename": "1.1.0", + "y18n": "4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + } + }, + "chokidar": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", + "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", + "dev": true, + "requires": { + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.1.0" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, + "chrome-trace-event": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-0.1.3.tgz", + "integrity": "sha512-sjndyZHrrWiu4RY7AkHgjn80GfAM2ZSzUkZLV/Js59Ldmh6JDThf0SUmOHU53rFu2rVxxfCzJ30Ukcfch3Gb/A==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "1.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "connect": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", + "integrity": "sha1-+43ee6B2OHfQ7J352sC0tA5yx9o=", + "requires": { + "debug": "2.6.9", + "finalhandler": "1.0.6", + "parseurl": "1.3.2", + "utils-merge": "1.0.1" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "md5.js": "1.3.4", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "inherits": "2.0.3", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.1", + "sha.js": "2.4.11" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "1.0.1", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.3", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "diffie-hellman": "5.0.3", + "inherits": "2.0.3", + "pbkdf2": "3.0.16", + "public-encrypt": "4.0.2", + "randombytes": "2.0.6", + "randomfill": "1.0.4" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "miller-rabin": "4.0.1", + "randombytes": "2.0.6" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "stream-shift": "1.0.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz", + "integrity": "sha512-jox/62b2GofV1qTUQTMPEJSDIGycS43evqYzD/KVtEb9OCoki9cnacUPxCrZa7JfPzZSYOCZhu9O9luaMxAX8g==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "memory-fs": "0.4.1", + "tapable": "1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "0.1.6", + "readable-stream": "2.3.3" + } + } + } + }, + "errno": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.6.tgz", + "integrity": "sha512-IsORQDpaaSwcDP4ZZnHxgE85werpo34VYn1Ud3mq+eUsF593faR8oCZNXrROVkpFu2TsbrNhHin0aUrTsQ9vNw==", + "requires": { + "prr": "1.0.1" + } + }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.1", + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "1.3.4", + "safe-buffer": "5.1.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "finalhandler": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", + "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "1.0.1", + "make-dir": "1.3.0", + "pkg-dir": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.3.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "1.1.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "ieee754": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", + "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "1.11.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-odd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", + "dev": true, + "requires": { + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "3.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "1.0.1" + } + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + } + }, + "memory-fs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", + "requires": { + "errno": "0.1.6", + "readable-stream": "2.3.3" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "2.0.1", + "pumpify": "1.5.1", + "stream-each": "1.2.2", + "through2": "2.0.3" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nanomatch": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, + "neo-async": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz", + "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==", + "dev": true + }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "1.4.1", + "browserify-zlib": "0.2.0", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "domain-browser": "1.2.0", + "events": "1.1.1", + "https-browserify": "1.0.0", + "os-browserify": "0.3.0", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.3", + "stream-browserify": "2.0.1", + "stream-http": "2.8.2", + "string_decoder": "1.0.3", + "timers-browserify": "2.0.10", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "1.2.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "4.10.1", + "browserify-aes": "1.2.0", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.16" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", + "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "dev": true, + "requires": { + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.1", + "sha.js": "2.4.11" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", + "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "parse-asn1": "5.1.1", + "randombytes": "2.0.6" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "3.6.0", + "inherits": "2.0.3", + "pump": "2.0.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "2.0.6", + "safe-buffer": "5.1.1" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.3", + "set-immediate-shim": "1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "1.2.0" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "0.1.15" + } + }, + "schema-utils": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", + "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "dev": true, + "requires": { + "ajv": "6.5.0", + "ajv-keywords": "3.2.0" + } + }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.2", + "use": "3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "2.1.1", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, + "stream-each": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" + } + }, + "stream-http": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.2.tgz", + "integrity": "sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==", + "dev": true, + "requires": { + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tapable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", + "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.3", + "xtend": "4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "1.0.5" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", + "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "2.13.0", + "source-map": "0.6.1" + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz", + "integrity": "sha512-hIQJ1yxAPhEA2yW/i7Fr+SXZVMp+VEI3d42RTHBgQd2yhp/1UdBcR3QEWPV5ahBxlqQDMEMTuTEvDHSFINfwSw==", + "dev": true, + "requires": { + "cacache": "10.0.4", + "find-cache-dir": "1.0.0", + "schema-utils": "0.4.5", + "serialize-javascript": "1.5.0", + "source-map": "0.6.1", + "uglify-es": "3.3.9", + "webpack-sources": "1.1.0", + "worker-farm": "1.6.0" + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", + "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "dev": true, + "requires": { + "unique-slug": "2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, + "requires": { + "imurmurhash": "0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "2.1.1" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "use": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", + "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "2.0.3", + "graceful-fs": "4.1.11", + "neo-async": "2.5.1" + } + }, + "webpack": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.5.0.tgz", + "integrity": "sha512-6GrZsvQJnG7o7mjbfjp6s5CyMfdopjt1A/X8LcYwceis9ySjqBX6Lusso2wNZ06utHj2ZvfL6L3f7hfgVeJP6g==", + "dev": true, + "requires": { + "acorn": "5.5.3", + "acorn-dynamic-import": "3.0.0", + "ajv": "6.5.0", + "ajv-keywords": "3.2.0", + "chrome-trace-event": "0.1.3", + "enhanced-resolve": "4.0.0", + "eslint-scope": "3.7.1", + "loader-runner": "2.3.0", + "loader-utils": "1.1.0", + "memory-fs": "0.4.1", + "micromatch": "3.1.10", + "mkdirp": "0.5.1", + "neo-async": "2.5.1", + "node-libs-browser": "2.1.0", + "schema-utils": "0.4.5", + "tapable": "1.0.0", + "uglifyjs-webpack-plugin": "1.2.5", + "watchpack": "1.6.0", + "webpack-sources": "1.1.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "0.1.6", + "readable-stream": "2.3.3" + } + } + } + }, + "webpack-node-externals": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.6.0.tgz", + "integrity": "sha1-Iyxi7GCSsQBjWj0p2DwXRxKN+b0=" + }, + "webpack-sources": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", + "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "dev": true, + "requires": { + "source-list-map": "2.0.0", + "source-map": "0.6.1" + } + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "0.1.7" + }, + "dependencies": { + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "1.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package.json new file mode 100644 index 0000000000..b9b956c287 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package.json @@ -0,0 +1,38 @@ +{ + "name": "aspnet-webpack", + "version": "3.0.0", + "description": "Helpers for using Webpack in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.", + "main": "index.js", + "scripts": { + "prepublish": "rimraf *.d.ts && tsc && echo 'Finished building NPM package \"aspnet-webpack\"'", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aspnet/JavaScriptServices/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/aspnet/JavaScriptServices.git" + }, + "dependencies": { + "connect": "^3.4.1", + "es6-promise": "^3.1.2", + "memory-fs": "^0.3.0", + "require-from-string": "^1.1.0", + "webpack-node-externals": "^1.4.3" + }, + "devDependencies": { + "@types/connect": "^3.4.30", + "@types/node": "^6.0.42", + "@types/webpack": "^4.1.3", + "rimraf": "^2.5.4", + "typescript": "^2.0.0", + "webpack": "^4.5.0" + }, + "peerDependencies": { + "webpack": "^1.13.2 || ^2.1.0-beta || ^3.0.0 || ^4.0.0", + "webpack-dev-middleware": "^1.8.4 || ^3.0.0" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/LoadViaWebpack.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/LoadViaWebpack.ts new file mode 100644 index 0000000000..0c75afc8bf --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/LoadViaWebpack.ts @@ -0,0 +1,146 @@ +// When you're using Webpack, it's often convenient to be able to require modules from regular JavaScript +// and have them transformed by Webpack. This is especially useful when doing ASP.NET server-side prerendering, +// because it means your boot module can use whatever source language you like (e.g., TypeScript), and means +// that your loader plugins (e.g., require('./mystyles.less')) work in exactly the same way on the server as +// on the client. +import 'es6-promise'; +import * as path from 'path'; +import * as webpack from 'webpack'; +import { requireNewCopy } from './RequireNewCopy'; + +// Strange import syntax to work around https://github.com/Microsoft/TypeScript/issues/2719 +import { requirefromstring } from './typings/require-from-string'; +import { memoryfs } from './typings/memory-fs'; +const nodeExternals = require('webpack-node-externals'); +const requireFromString = require('require-from-string') as typeof requirefromstring.requireFromString; +const MemoryFS = require('memory-fs') as typeof memoryfs.MemoryFS; + +// Ensure we only go through the compile process once per [config, module] pair +const loadViaWebpackPromisesCache: { [key: string]: any } = {}; + +export interface LoadViaWebpackCallback { + (error: any, result: T): void; +} + +export function loadViaWebpack(webpackConfigPath: string, modulePath: string, callback: LoadViaWebpackCallback) { + const cacheKey = JSON.stringify(webpackConfigPath) + JSON.stringify(modulePath); + if (!(cacheKey in loadViaWebpackPromisesCache)) { + loadViaWebpackPromisesCache[cacheKey] = loadViaWebpackNoCache(webpackConfigPath, modulePath); + } + loadViaWebpackPromisesCache[cacheKey].then(result => { + callback(null, result); + }, error => { + callback(error, null); + }) +} + +function setExtension(filePath: string, newExtension: string) { + const oldExtensionIfAny = path.extname(filePath); + const basenameWithoutExtension = path.basename(filePath, oldExtensionIfAny); + return path.join(path.dirname(filePath), basenameWithoutExtension) + newExtension; +} + +function loadViaWebpackNoCache(webpackConfigPath: string, modulePath: string) { + return new Promise((resolve, reject) => { + // Load the Webpack config and make alterations needed for loading the output into Node + const webpackConfig: webpack.Configuration = requireNewCopy(webpackConfigPath); + webpackConfig.entry = modulePath; + webpackConfig.target = 'node'; + + // Make sure we preserve the 'path' and 'publicPath' config values if specified, as these + // can affect the build output (e.g., when using 'file' loader, the publicPath value gets + // set as a prefix on output paths). + webpackConfig.output = webpackConfig.output || {}; + webpackConfig.output.path = webpackConfig.output.path || '/'; + webpackConfig.output.filename = 'webpack-output.js'; + webpackConfig.output.libraryTarget = 'commonjs'; + const outputVirtualPath = path.join(webpackConfig.output.path, webpackConfig.output.filename); + + // In Node, we want any JavaScript modules under /node_modules/ to be loaded natively and not bundled into the + // output (partly because it's faster, but also because otherwise there'd be different instances of modules + // depending on how they were loaded, which could lead to errors). + // --- + // NOTE: We have to use webpack-node-externals rather than webpack-externals-plugin because + // webpack-externals-plugin doesn't correctly resolve relative paths, which means you can't + // use css-loader, since tries to require('./../../node_modules/css-loader/lib/css-base.js') (see #132) + // --- + // So, ensure that webpackConfig.externals is an array, and push WebpackNodeExternals into it: + let externalsArray: any[] = (webpackConfig.externals as any[]) || []; + if (!(externalsArray instanceof Array)) { + externalsArray = [externalsArray]; + } + webpackConfig.externals = externalsArray; + externalsArray.push(nodeExternals({ + // However, we do *not* want to treat non-JS files under /node_modules/ as externals (i.e., things + // that should be loaded via regular CommonJS 'require' statements). For example, if you reference + // a .css file inside an NPM module (e.g., require('somepackage/somefile.css')), then we do need to + // load that via Webpack rather than as a regular CommonJS module. + // + // So, configure webpack-externals-plugin to 'whitelist' (i.e., not treat as external) any file + // that has an extension other than .js. Also, since some libraries such as font-awesome refer to + // their own files with cache-busting querystrings (e.g., (url('./something.css?v=4.1.2'))), we + // need to treat '?' as an alternative 'end of filename' marker. + // + // The complex, awkward regex can be eliminated once webpack-externals-plugin merges + // https://github.com/liady/webpack-node-externals/pull/12 + // + // This regex looks for at least one dot character that is *not* followed by "js", but + // is followed by some series of non-dot characters followed by : + whitelist: [/\.(?!js(\?|$))([^.]+(\?|$))/] + })); + + // The CommonsChunkPlugin is not compatible with a CommonJS environment like Node, nor is it needed in that case + const ChunkPlugin = webpack.optimize['CommonsChunkPlugin']; + if (ChunkPlugin !== undefined) { + webpackConfig.plugins = webpackConfig.plugins.filter(plugin => { + return !(plugin instanceof ChunkPlugin); + }); + } + + // The typical use case for DllReferencePlugin is for referencing vendor modules. In a Node + // environment, it doesn't make sense to load them from a DLL bundle, nor would that even + // work, because then you'd get different module instances depending on whether a module + // was referenced via a normal CommonJS 'require' or via Webpack. So just remove any + // DllReferencePlugin from the config. + // If someone wanted to load their own DLL modules (not an NPM module) via DllReferencePlugin, + // that scenario is not supported today. We would have to add some extra option to the + // asp-prerender tag helper to let you specify a list of DLL bundles that should be evaluated + // in this context. But even then you'd need special DLL builds for the Node environment so that + // external dependencies were fetched via CommonJS requires, so it's unclear how that could work. + // The ultimate escape hatch here is just prebuilding your code as part of the application build + // and *not* using asp-prerender-webpack-config at all, then you can do anything you want. + webpackConfig.plugins = webpackConfig.plugins.filter(plugin => { + // DllReferencePlugin is missing from webpack.d.ts for some reason, hence referencing it + // as a key-value object property + return !(plugin instanceof webpack['DllReferencePlugin']); + }); + + // Create a compiler instance that stores its output in memory, then load its output + const compiler = webpack(webpackConfig); + compiler.outputFileSystem = new MemoryFS(); + compiler.run((err, stats) => { + if (err) { + reject(err); + } else { + // We're in a callback, so need an explicit try/catch to propagate any errors up the promise chain + try { + if (stats.hasErrors()) { + throw new Error('Webpack compilation reported errors. Compiler output follows: ' + + stats.toString({ chunks: false })); + } + + // The dynamically-built module will only appear in node-inspector if it has some nonempty + // file path. The following value is arbitrary (since there's no real compiled file on disk) + // but is sufficient to enable debugging. + const fakeModulePath = setExtension(modulePath, '.js'); + + const fileContent = compiler.outputFileSystem.readFileSync(outputVirtualPath, 'utf8'); + const moduleInstance = requireFromString(fileContent, fakeModulePath); + resolve(moduleInstance); + } catch(ex) { + reject(ex); + } + } + }); + }); +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/RequireNewCopy.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/RequireNewCopy.ts new file mode 100644 index 0000000000..940c83ef92 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/RequireNewCopy.ts @@ -0,0 +1,22 @@ +export function requireNewCopy(moduleNameOrPath: string): any { + // Store a reference to whatever's in the 'require' cache, + // so we don't permanently destroy it, and then ensure there's + // no cache entry for this module + const resolvedModule = require.resolve(moduleNameOrPath); + const wasCached = resolvedModule in require.cache; + let cachedInstance; + if (wasCached) { + cachedInstance = require.cache[resolvedModule]; + delete require.cache[resolvedModule]; + } + + try { + // Return a new copy + return require(resolvedModule); + } finally { + // Restore the cached entry, if any + if (wasCached) { + require.cache[resolvedModule] = cachedInstance; + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts new file mode 100644 index 0000000000..324f5db878 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts @@ -0,0 +1,396 @@ +import * as connect from 'connect'; +import * as webpack from 'webpack'; +import * as url from 'url'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as querystring from 'querystring'; +import { requireNewCopy } from './RequireNewCopy'; +import { hasSufficientPermissions } from './WebpackTestPermissions'; + +export type CreateDevServerResult = { + Port: number, + PublicPaths: string[] +}; + +export interface CreateDevServerCallback { + (error: any, result: CreateDevServerResult): void; +} + +// These are the options passed by WebpackDevMiddleware.cs +interface CreateDevServerOptions { + webpackConfigPath: string; + suppliedOptions: DevServerOptions; + hotModuleReplacementEndpointUrl: string; +} + +type EsModuleExports = { __esModule: true, default: T }; +type StringMap = [(key: string) => T]; + +// These are the options configured in C# and then JSON-serialized, hence the C#-style naming +interface DevServerOptions { + HotModuleReplacement: boolean; + HotModuleReplacementServerPort: number; + HotModuleReplacementClientOptions: StringMap; + ReactHotModuleReplacement: boolean; + EnvParam: any; +} + +// Interface as defined in es6-promise +interface Thenable { + then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => U | Thenable): Thenable; + then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => void): Thenable; +} + +// We support these four kinds of webpack.config.js export +type WebpackConfigOrArray = webpack.Configuration | webpack.Configuration[]; +type WebpackConfigOrArrayOrThenable = WebpackConfigOrArray | Thenable; +interface WebpackConfigFunc { + (env?: any): WebpackConfigOrArrayOrThenable; +} +type WebpackConfigExport = WebpackConfigOrArrayOrThenable | WebpackConfigFunc; +type WebpackConfigModuleExports = WebpackConfigExport | EsModuleExports; + +function isThenable(obj: any): obj is Thenable { + return obj && typeof (>obj).then === 'function'; +} + +function attachWebpackDevMiddleware(app: any, webpackConfig: webpack.Configuration, enableHotModuleReplacement: boolean, enableReactHotModuleReplacement: boolean, hmrClientOptions: StringMap, hmrServerEndpoint: string) { + // Build the final Webpack config based on supplied options + if (enableHotModuleReplacement) { + // For this, we only support the key/value config format, not string or string[], since + // those ones don't clearly indicate what the resulting bundle name will be + const entryPoints = webpackConfig.entry; + const isObjectStyleConfig = entryPoints + && typeof entryPoints === 'object' + && !(entryPoints instanceof Array); + if (!isObjectStyleConfig) { + throw new Error('To use HotModuleReplacement, your webpack config must specify an \'entry\' value as a key-value object (e.g., "entry: { main: \'ClientApp/boot-client.ts\' }")'); + } + + // Augment all entry points so they support HMR (unless they already do) + Object.getOwnPropertyNames(entryPoints).forEach(entryPointName => { + const webpackHotMiddlewareEntryPoint = 'webpack-hot-middleware/client'; + const webpackHotMiddlewareOptions = '?' + querystring.stringify(hmrClientOptions); + if (typeof entryPoints[entryPointName] === 'string') { + entryPoints[entryPointName] = [webpackHotMiddlewareEntryPoint + webpackHotMiddlewareOptions, entryPoints[entryPointName]]; + } else if (firstIndexOfStringStartingWith(entryPoints[entryPointName], webpackHotMiddlewareEntryPoint) < 0) { + entryPoints[entryPointName].unshift(webpackHotMiddlewareEntryPoint + webpackHotMiddlewareOptions); + } + + // Now also inject eventsource polyfill so this can work on IE/Edge (unless it's already there) + // To avoid this being a breaking change for everyone who uses aspnet-webpack, we only do this if you've + // referenced event-source-polyfill in your package.json. Note that having event-source-polyfill available + // on the server in node_modules doesn't imply that you've also included it in your client-side bundle, + // but the converse is true (if it's not in node_modules, then you obviously aren't trying to use it at + // all, so it would definitely not work to take a dependency on it). + const eventSourcePolyfillEntryPoint = 'event-source-polyfill'; + if (npmModuleIsPresent(eventSourcePolyfillEntryPoint)) { + const entryPointsArray: string[] = entryPoints[entryPointName]; // We know by now that it's an array, because if it wasn't, we already wrapped it in one + if (entryPointsArray.indexOf(eventSourcePolyfillEntryPoint) < 0) { + const webpackHmrIndex = firstIndexOfStringStartingWith(entryPointsArray, webpackHotMiddlewareEntryPoint); + if (webpackHmrIndex < 0) { + // This should not be possible, since we just added it if it was missing + throw new Error('Cannot find ' + webpackHotMiddlewareEntryPoint + ' in entry points array: ' + entryPointsArray); + } + + // Insert the polyfill just before the HMR entrypoint + entryPointsArray.splice(webpackHmrIndex, 0, eventSourcePolyfillEntryPoint); + } + } + }); + + webpackConfig.plugins = [].concat(webpackConfig.plugins || []); // Be sure not to mutate the original array, as it might be shared + webpackConfig.plugins.push( + new webpack.HotModuleReplacementPlugin() + ); + + // Set up React HMR support if requested. This requires the 'aspnet-webpack-react' package. + if (enableReactHotModuleReplacement) { + let aspNetWebpackReactModule: any; + try { + aspNetWebpackReactModule = require('aspnet-webpack-react'); + } catch(ex) { + throw new Error('ReactHotModuleReplacement failed because of an error while loading \'aspnet-webpack-react\'. Error was: ' + ex.stack); + } + + aspNetWebpackReactModule.addReactHotModuleReplacementBabelTransform(webpackConfig); + } + } + + // Attach Webpack dev middleware and optional 'hot' middleware + const compiler = webpack(webpackConfig); + app.use(require('webpack-dev-middleware')(compiler, { + noInfo: true, + stats: webpackConfig.stats, + publicPath: ensureLeadingSlash(webpackConfig.output.publicPath), + watchOptions: webpackConfig.watchOptions + })); + + // After each compilation completes, copy the in-memory filesystem to disk. + // This is needed because the debuggers in both VS and VS Code assume that they'll be able to find + // the compiled files on the local disk (though it would be better if they got the source file from + // the browser they are debugging, which would be more correct and make this workaround unnecessary). + // Without this, Webpack plugins like HMR that dynamically modify the compiled output in the dev + // middleware's in-memory filesystem only (and not on disk) would confuse the debugger, because the + // file on disk wouldn't match the file served to the browser, and the source map line numbers wouldn't + // match up. Breakpoints would either not be hit, or would hit the wrong lines. + const copy = stats => copyRecursiveToRealFsSync(compiler.outputFileSystem, '/', [/\.hot-update\.(js|json|js\.map)$/]); + if (compiler.hooks) { + compiler.hooks.done.tap('aspnet-webpack', copy); + } else { + compiler.plugin('done', copy); + } + + if (enableHotModuleReplacement) { + let webpackHotMiddlewareModule; + try { + webpackHotMiddlewareModule = require('webpack-hot-middleware'); + } catch (ex) { + throw new Error('HotModuleReplacement failed because of an error while loading \'webpack-hot-middleware\'. Error was: ' + ex.stack); + } + app.use(workaroundIISExpressEventStreamFlushingIssue(hmrServerEndpoint)); + app.use(webpackHotMiddlewareModule(compiler, { + path: hmrServerEndpoint + })); + } +} + +function workaroundIISExpressEventStreamFlushingIssue(path: string): connect.NextHandleFunction { + // IIS Express makes HMR seem very slow, because when it's reverse-proxying an EventStream response + // from Kestrel, it doesn't pass through the lines to the browser immediately, even if you're calling + // response.Flush (or equivalent) in your ASP.NET Core code. For some reason, it waits until the following + // line is sent. By default, that wouldn't be until the next HMR heartbeat, which can be up to 5 seconds later. + // In effect, it looks as if your code is taking 5 seconds longer to compile than it really does. + // + // As a workaround, this connect middleware intercepts requests to the HMR endpoint, and modifies the response + // stream so that all EventStream 'data' lines are immediately followed with a further blank line. This is + // harmless in non-IIS-Express cases, because it's OK to have extra blank lines in an EventStream response. + // The implementation is simplistic - rather than using a true stream reader, we just patch the 'write' + // method. This relies on webpack's HMR code always writing complete EventStream messages with a single + // 'write' call. That works fine today, but if webpack's HMR code was changed, this workaround might have + // to be updated. + const eventStreamLineStart = /^data\:/; + return (req, res, next) => { + // We only want to interfere with requests to the HMR endpoint, so check this request matches + const urlMatchesPath = (req.url === path) || (req.url.split('?', 1)[0] === path); + if (urlMatchesPath) { + const origWrite = res.write; + res.write = function (chunk) { + const result = origWrite.apply(this, arguments); + + // We only want to interfere with actual EventStream data lines, so check it is one + if (typeof (chunk) === 'string') { + if (eventStreamLineStart.test(chunk) && chunk.charAt(chunk.length - 1) === '\n') { + origWrite.call(this, '\n\n'); + } + } + + return result; + } + } + + return next(); + }; +} + +function copyRecursiveToRealFsSync(from: typeof fs, rootDir: string, exclude: RegExp[]) { + from.readdirSync(rootDir).forEach(filename => { + const fullPath = pathJoinSafe(rootDir, filename); + const shouldExclude = exclude.filter(re => re.test(fullPath)).length > 0; + if (!shouldExclude) { + const fileStat = from.statSync(fullPath); + if (fileStat.isFile()) { + const fileBuf = from.readFileSync(fullPath); + fs.writeFileSync(fullPath, fileBuf); + } else if (fileStat.isDirectory()) { + if (!fs.existsSync(fullPath)) { + fs.mkdirSync(fullPath); + } + copyRecursiveToRealFsSync(from, fullPath, exclude); + } + } + }); +} + +function ensureLeadingSlash(value: string) { + if (value !== null && value.substring(0, 1) !== '/') { + value = '/' + value; + } + + return value; +} + +function pathJoinSafe(rootPath: string, filePath: string) { + // On Windows, MemoryFileSystem's readdirSync output produces directory entries like 'C:' + // which then trigger errors if you call statSync for them. Avoid this by detecting drive + // names at the root, and adding a backslash (so 'C:' becomes 'C:\', which works). + if (rootPath === '/' && path.sep === '\\' && filePath.match(/^[a-z0-9]+\:$/i)) { + return filePath + '\\'; + } else { + return path.join(rootPath, filePath); + } +} + +function beginWebpackWatcher(webpackConfig: webpack.Configuration) { + const compiler = webpack(webpackConfig); + compiler.watch(webpackConfig.watchOptions || {}, (err, stats) => { + // The default error reporter is fine for now, but could be customized here in the future if desired + }); +} + +export function createWebpackDevServer(callback: CreateDevServerCallback, optionsJson: string) { + const options: CreateDevServerOptions = JSON.parse(optionsJson); + + // Enable TypeScript loading if the webpack config is authored in TypeScript + if (path.extname(options.webpackConfigPath) === '.ts') { + try { + require('ts-node/register'); + } catch (ex) { + throw new Error('Error while attempting to enable support for Webpack config file written in TypeScript. Make sure your project depends on the "ts-node" NPM package. The underlying error was: ' + ex.stack); + } + } + + // See the large comment in WebpackTestPermissions.ts for details about this + if (!hasSufficientPermissions()) { + console.log('WARNING: Webpack dev middleware is not enabled because the server process does not have sufficient permissions. You should either remove the UseWebpackDevMiddleware call from your code, or to make it work, give your server process user account permission to write to your application directory and to read all ancestor-level directories.'); + callback(null, { + Port: 0, + PublicPaths: [] + }); + return; + } + + // Read the webpack config's export, and normalize it into the more general 'array of configs' format + const webpackConfigModuleExports: WebpackConfigModuleExports = requireNewCopy(options.webpackConfigPath); + let webpackConfigExport = (webpackConfigModuleExports as EsModuleExports<{}>).__esModule === true + ? (webpackConfigModuleExports as EsModuleExports).default + : (webpackConfigModuleExports as WebpackConfigExport); + + if (webpackConfigExport instanceof Function) { + // If you export a function, then Webpack convention is that it takes zero or one param, + // and that param is called `env` and reflects the `--env.*` args you can specify on + // the command line (e.g., `--env.prod`). + // When invoking it via WebpackDevMiddleware, we let you configure the `env` param in + // your Startup.cs. + webpackConfigExport = webpackConfigExport(options.suppliedOptions.EnvParam); + } + + const webpackConfigThenable = isThenable(webpackConfigExport) + ? webpackConfigExport + : { then: callback => callback(webpackConfigExport) } as Thenable; + + webpackConfigThenable.then(webpackConfigResolved => { + const webpackConfigArray = webpackConfigResolved instanceof Array ? webpackConfigResolved : [webpackConfigResolved]; + + const enableHotModuleReplacement = options.suppliedOptions.HotModuleReplacement; + const enableReactHotModuleReplacement = options.suppliedOptions.ReactHotModuleReplacement; + if (enableReactHotModuleReplacement && !enableHotModuleReplacement) { + callback('To use ReactHotModuleReplacement, you must also enable the HotModuleReplacement option.', null); + return; + } + + // The default value, 0, means 'choose randomly' + const suggestedHMRPortOrZero = options.suppliedOptions.HotModuleReplacementServerPort || 0; + + const app = connect(); + const listener = app.listen(suggestedHMRPortOrZero, () => { + try { + // For each webpack config that specifies a public path, add webpack dev middleware for it + const normalizedPublicPaths: string[] = []; + webpackConfigArray.forEach(webpackConfig => { + if (webpackConfig.target === 'node') { + // For configs that target Node, it's meaningless to set up an HTTP listener, since + // Node isn't going to load those modules over HTTP anyway. It just loads them directly + // from disk. So the most relevant thing we can do with such configs is just write + // updated builds to disk, just like "webpack --watch". + beginWebpackWatcher(webpackConfig); + } else { + // For configs that target browsers, we can set up an HTTP listener, and dynamically + // modify the config to enable HMR etc. This just requires that we have a publicPath. + const publicPath = (webpackConfig.output.publicPath || '').trim(); + if (!publicPath) { + throw new Error('To use the Webpack dev server, you must specify a value for \'publicPath\' on the \'output\' section of your webpack config (for any configuration that targets browsers)'); + } + const publicPathNoTrailingSlash = removeTrailingSlash(publicPath); + normalizedPublicPaths.push(publicPathNoTrailingSlash); + + // This is the URL the client will connect to, except that since it's a relative URL + // (no leading slash), Webpack will resolve it against the runtime URL + // plus it also adds the publicPath + const hmrClientEndpoint = removeLeadingSlash(options.hotModuleReplacementEndpointUrl); + + // This is the URL inside the Webpack middleware Node server that we'll proxy to. + // We have to prefix with the public path because Webpack will add the publicPath + // when it resolves hmrClientEndpoint as a relative URL. + const hmrServerEndpoint = ensureLeadingSlash(publicPathNoTrailingSlash + options.hotModuleReplacementEndpointUrl); + + // We always overwrite the 'path' option as it needs to match what the .NET side is expecting + const hmrClientOptions = options.suppliedOptions.HotModuleReplacementClientOptions || >{}; + hmrClientOptions['path'] = hmrClientEndpoint; + + const dynamicPublicPathKey = 'dynamicPublicPath'; + if (!(dynamicPublicPathKey in hmrClientOptions)) { + // dynamicPublicPath default to true, so we can work with nonempty pathbases (virtual directories) + hmrClientOptions[dynamicPublicPathKey] = true; + } else { + // ... but you can set it to any other value explicitly if you want (e.g., false) + hmrClientOptions[dynamicPublicPathKey] = JSON.parse(hmrClientOptions[dynamicPublicPathKey]); + } + + attachWebpackDevMiddleware(app, webpackConfig, enableHotModuleReplacement, enableReactHotModuleReplacement, hmrClientOptions, hmrServerEndpoint); + } + }); + + // Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here + callback(null, { + Port: listener.address().port, + PublicPaths: normalizedPublicPaths + }); + } catch (ex) { + callback(ex.stack, null); + } + }); + }, + err => callback(err.stack, null) + ); +} + +function removeLeadingSlash(str: string) { + if (str.indexOf('/') === 0) { + str = str.substring(1); + } + + return str; +} + +function removeTrailingSlash(str: string) { + if (str.lastIndexOf('/') === str.length - 1) { + str = str.substring(0, str.length - 1); + } + + return str; +} + +function getPath(publicPath: string) { + return url.parse(publicPath).path; +} + +function firstIndexOfStringStartingWith(array: string[], prefixToFind: string) { + for (let index = 0; index < array.length; index++) { + const candidate = array[index]; + if ((typeof candidate === 'string') && (candidate.substring(0, prefixToFind.length) === prefixToFind)) { + return index; + } + } + + return -1; // Not found +} + +function npmModuleIsPresent(moduleName: string) { + try { + require.resolve(moduleName); + return true; + } catch (ex) { + return false; + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackTestPermissions.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackTestPermissions.ts new file mode 100644 index 0000000000..5747614715 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackTestPermissions.ts @@ -0,0 +1,58 @@ +import * as fs from 'fs'; +import * as path from 'path'; +const isWindows = /^win/.test(process.platform); + +// On Windows, Node (still as of v8.1.3) has an issue whereby, when locating JavaScript modules +// on disk, it walks up the directory hierarchy to the disk root, testing whether each directory +// is a symlink or not. This fails with an exception if the process doesn't have permission to +// read those directories. This is a problem when hosting in full IIS, because in typical cases +// the process does not have read permission for higher-level directories. +// +// NodeServices itself works around this by injecting a patched version of Node's 'lstat' API that +// suppresses these irrelevant errors during module loads. This covers most scenarios, but isn't +// enough to make Webpack dev middleware work, because typical Webpack configs use loaders such as +// 'awesome-typescript-loader', which works by forking a child process to do some of its work. The +// child process does not get the patched 'lstat', and hence fails. It's an especially bad failure, +// because the Webpack compiler doesn't even surface the exception - it just never completes the +// compilation process, causing the application to hang indefinitely. +// +// Additionally, Webpack dev middleware will want to write its output to disk, which is also going +// to fail in a typical IIS process, because you won't have 'write' permission to the app dir by +// default. We have to actually write the build output to disk (and not purely keep it in the in- +// memory file system) because the server-side prerendering Node instance is a separate process +// that only knows about code changes when it sees the compiled files on disk change. +// +// In the future, we'll hopefully get Node to fix its underlying issue, and figure out whether VS +// could give 'write' access to the app dir when launching sites in IIS. But until then, disable +// Webpack dev middleware if we detect the server process doesn't have the necessary permissions. + +export function hasSufficientPermissions() { + if (isWindows) { + return canReadDirectoryAndAllAncestors(process.cwd()); + } else { + return true; + } +} + +function canReadDirectoryAndAllAncestors(dir: string): boolean { + if (!canReadDirectory(dir)) { + return false; + } + + const parentDir = path.resolve(dir, '..'); + if (parentDir === dir) { + // There are no more parent directories - we've reached the disk root + return true; + } else { + return canReadDirectoryAndAllAncestors(parentDir); + } +} + +function canReadDirectory(dir: string): boolean { + try { + fs.statSync(dir); + return true; + } catch(ex) { + return false; + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/index.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/index.ts new file mode 100644 index 0000000000..2d5ff4b856 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/index.ts @@ -0,0 +1,2 @@ +export { createWebpackDevServer } from './WebpackDevMiddleware'; +export { loadViaWebpack } from './LoadViaWebpack'; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/memory-fs.d.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/memory-fs.d.ts new file mode 100644 index 0000000000..6a2d353ed6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/memory-fs.d.ts @@ -0,0 +1,3 @@ +export namespace memoryfs { + export class MemoryFS {} +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/require-from-string.d.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/require-from-string.d.ts new file mode 100644 index 0000000000..ba41884c7d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/require-from-string.d.ts @@ -0,0 +1,3 @@ +export namespace requirefromstring { + export function requireFromString(fileContent: string, filename?: string): T; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/webpack-node-externals.d.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/webpack-node-externals.d.ts new file mode 100644 index 0000000000..0bc9e4506d --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/typings/webpack-node-externals.d.ts @@ -0,0 +1,2 @@ +declare module 'webpack-node-externals' { +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/tsconfig.json new file mode 100644 index 0000000000..edd184de47 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "commonjs", + "target": "es5", + "declaration": true, + "outDir": ".", + "lib": ["es2015"], + "types": ["node"] + }, + "files": [ + "src/index.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/.gitignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/.gitignore new file mode 100644 index 0000000000..025755a469 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/.gitignore @@ -0,0 +1,3 @@ +/node_modules/ +/*.js +/*.d.ts diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/.npmignore b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/.npmignore new file mode 100644 index 0000000000..858cdc4c3b --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/.npmignore @@ -0,0 +1,3 @@ +!/*.js +!/*.d.ts +/typings/ diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/LICENSE.txt b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/LICENSE.txt new file mode 100644 index 0000000000..0bdc1962b6 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/README.md b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/README.md new file mode 100644 index 0000000000..1333ed77b7 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/README.md @@ -0,0 +1 @@ +TODO diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/package.json new file mode 100644 index 0000000000..b22be854a2 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/package.json @@ -0,0 +1,29 @@ +{ + "name": "domain-task", + "version": "3.0.3", + "description": "Tracks outstanding operations for a logical thread of execution", + "main": "index.js", + "scripts": { + "prepublish": "rimraf *.d.ts && tsc && echo 'Finished building NPM package \"domain-task\"'", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aspnet/JavaScriptServices/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/aspnet/JavaScriptServices.git" + }, + "dependencies": { + "domain-context": "^0.5.1", + "is-absolute-url": "^2.1.0", + "isomorphic-fetch": "^2.2.1" + }, + "devDependencies": { + "@types/node": "^6.0.42", + "rimraf": "^2.5.4", + "typescript": "^2.2.1" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/domain-context.d.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/domain-context.d.ts new file mode 100644 index 0000000000..1114f929fd --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/domain-context.d.ts @@ -0,0 +1,9 @@ +declare module 'domain' { + var active: Domain; +} + +declare module 'domain-context' { + function get(key: string): any; + function set(key: string, value: any): void; + function runInNewDomain(code: () => void): void; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/fetch.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/fetch.ts new file mode 100644 index 0000000000..fa576cf679 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/fetch.ts @@ -0,0 +1,105 @@ +import * as url from 'url'; +import * as domain from 'domain'; +import * as domainContext from 'domain-context'; +import * as isAbsoluteUrl from 'is-absolute-url'; +import { baseUrl } from './main'; +const isomorphicFetch = require('isomorphic-fetch'); +const isNode = typeof process === 'object' && process.versions && !!process.versions.node; +const nodeHttps = isNode && require('https'); +const isHttpsRegex = /^https\:/; + +function issueRequest(baseUrl: string, req: string | Request, init?: RequestInit): Promise { + const reqUrl = (req instanceof Request) ? req.url : req; + const isRelativeUrl = reqUrl && !isAbsoluteUrl(reqUrl); + + // Resolve relative URLs + if (baseUrl) { + if (req instanceof Request) { + const reqAsRequest = req as Request; + (reqAsRequest as any).url = url.resolve(baseUrl, reqAsRequest.url); + } else { + req = url.resolve(baseUrl, req as string); + } + } else if (isNode) { + // TODO: Consider only throwing if it's a relative URL, since absolute ones would work fine + throw new Error(` + When running outside the browser (e.g., in Node.js), you must specify a base URL + before invoking domain-task's 'fetch' wrapper. + Example: + import { baseUrl } from 'domain-task/fetch'; + baseUrl('http://example.com'); // Relative URLs will be resolved against this + `); + } + + init = applyHttpsAgentPolicy(init, isRelativeUrl, baseUrl); + return isomorphicFetch(req, init); +} + +function applyHttpsAgentPolicy(init: RequestInit, isRelativeUrl: boolean, baseUrl: string): RequestInit { + // HTTPS is awkward in Node because it uses a built-in list of CAs, rather than recognizing + // the OS's system-level CA list. There are dozens of issues filed against Node about this, + // but still (as of v8.0.0) no resolution besides manually duplicating your CA config. + // + // The biggest problem for typical isomorphic-SPA development this causes is that if you're + // using a self-signed localhost cert in development, Node won't be able to make API calls + // to it (e.g., https://github.com/aspnet/JavaScriptServices/issues/1089). Developers could + // fix this by either manually configuring the cert in Node (which is extremely inconvenient, + // especially if multiple devs on a team have different self-signed localhost certs), or by + // disabling cert verification on their API requests. + // + // Fortunately, 'domain-task/fetch' knows when you're making a relative-URL request to your + // own web server (as opposed to an arbitrary request to anywhere else). In this specific case, + // there's no real point in cert verification, since the request never even leaves the machine + // so a MitM attack isn't meaningful. So by default, when your code is running in Node and + // is making a relative-URL request, *and* if you haven't explicitly configured any option + // for 'agent' (which would let you set up other HTTPS-handling policies), then we automatically + // disable cert verification for that request. + if (isNode && isRelativeUrl) { + const isHttps = baseUrl && isHttpsRegex.test(baseUrl); + if (isHttps) { + const hasAgentConfig = init && ('agent' in init); + if (!hasAgentConfig) { + const agentForRequest = new (nodeHttps.Agent)({ rejectUnauthorized: false }); + + init = init || {}; + (init as any).agent = agentForRequest; + } + } + } + + return init; +} + +export function fetch(url: string | Request, init?: RequestInit): Promise { + // As of domain-task 2.0.0, we no longer auto-add the 'fetch' promise to the current domain task list. + // This is because it's misleading to do so, and can result in race-condition bugs, e.g., + // https://github.com/aspnet/JavaScriptServices/issues/166 + // + // Consider this usage: + // + // import { fetch } from 'domain-task/fetch'; + // fetch(something).then(callback1).then(callback2) ...etc... .then(data => updateCriticalAppState); + // + // If we auto-add the very first 'fetch' promise to the domain task list, then the domain task completion + // callback might fire at any point among all the chained callbacks. If there are enough chained callbacks, + // it's likely to occur before the final 'updateCriticalAppState' one. Previously we thought it was enough + // for domain-task to use setTimeout(..., 0) so that its action occurred after all synchronously-scheduled + // chained promise callbacks, but this turns out not to be the case. Current versions of Node will run + // setTimeout-scheduled callbacks *before* setImmediate ones, if their timeout has elapsed. So even if you + // use setTimeout(..., 10), then this callback will run before setImmediate(...) if there were 10ms or more + // of CPU-blocking activity. In other words, a race condition. + // + // The correct design is for the final chained promise to be the thing added to the domain task list, but + // this can only be done by the developer and not baked into the 'fetch' API. The developer needs to write + // something like: + // + // var myTask = fetch(something).then(callback1).then(callback2) ...etc... .then(data => updateCriticalAppState); + // addDomainTask(myTask); + // + // ... so that the domain-tasks-completed callback never fires until after 'updateCriticalAppState'. + return issueRequest(baseUrl(), url, init); +} + +// Re-exporting baseUrl from this module for back-compatibility only +// Newer code that wants to access baseUrl should use the version exported from the root of this package +export { baseUrl } from './main'; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/index.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/index.ts new file mode 100644 index 0000000000..b6cb2ab2da --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/index.ts @@ -0,0 +1,3 @@ +// This file determines the top-level package exports +export { addTask, run, baseUrl } from './main'; +export { fetch } from './fetch'; diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/main.ts b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/main.ts new file mode 100644 index 0000000000..0a944d0cc4 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/main.ts @@ -0,0 +1,85 @@ +import * as domain from 'domain'; +import * as domainContext from 'domain-context'; + +// Not using symbols, because this may need to run in a version of Node.js that doesn't support them +const domainTasksStateKey = '__DOMAIN_TASKS'; +const domainTaskBaseUrlStateKey = '__DOMAIN_TASK_INTERNAL_FETCH_BASEURL__DO_NOT_REFERENCE_THIS__'; + +let noDomainBaseUrl: string; + +export function addTask(task: PromiseLike): PromiseLike { + if (task && domain.active) { + const state = domainContext.get(domainTasksStateKey) as DomainTasksState; + if (state) { + state.numRemainingTasks++; + task.then(() => { + // The application may have other listeners chained to this promise *after* + // this listener, which may in turn register further tasks. Since we don't + // want the combined task to complete until all the handlers for child tasks + // have finished, delay the response to give time for more tasks to be added + // synchronously. + setTimeout(() => { + state.numRemainingTasks--; + if (state.numRemainingTasks === 0 && !state.hasIssuedSuccessCallback) { + state.hasIssuedSuccessCallback = true; + setTimeout(() => { + state.completionCallback(/* error */ null); + }, 0); + } + }, 0); + }, (error) => { + state.completionCallback(error); + }); + } + } + + return task; +} + +export function run(codeToRun: () => T, completionCallback: (error: any) => void): T { + let synchronousResult: T; + domainContext.runInNewDomain(() => { + const state: DomainTasksState = { + numRemainingTasks: 0, + hasIssuedSuccessCallback: false, + completionCallback: domain.active.bind(completionCallback) + }; + + try { + domainContext.set(domainTasksStateKey, state); + synchronousResult = codeToRun(); + + // If no tasks were registered synchronously, then we're done already + if (state.numRemainingTasks === 0 && !state.hasIssuedSuccessCallback) { + state.hasIssuedSuccessCallback = true; + setTimeout(() => { + state.completionCallback(/* error */ null); + }, 0); + } + } catch(ex) { + state.completionCallback(ex); + } + }); + + return synchronousResult; +} + +export function baseUrl(url?: string): string { + if (url) { + if (domain.active) { + // There's an active domain (e.g., in Node.js), so associate the base URL with it + domainContext.set(domainTaskBaseUrlStateKey, url); + } else { + // There's no active domain (e.g., in browser), so there's just one shared base URL + noDomainBaseUrl = url; + } + } + + return domain.active ? domainContext.get(domainTaskBaseUrlStateKey) : noDomainBaseUrl; +} + +interface DomainTasksState { + numRemainingTasks: number; + hasIssuedSuccessCallback: boolean; + completionCallback: (error: any) => void; +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/tsconfig.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/tsconfig.json new file mode 100644 index 0000000000..518dc798e9 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "commonjs", + "target": "es5", + "declaration": true, + "outDir": ".", + "lib": ["es2015", "dom"] + }, + "files": [ + "src/index.ts", + "src/domain-context.d.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/package-lock.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/package-lock.json new file mode 100644 index 0000000000..e0a5a40366 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/package-lock.json @@ -0,0 +1,4244 @@ +{ + "name": "spaservices", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.2.tgz", + "integrity": "sha512-pwZnkVyCGJ3LsQ0/3flQK5lCFao4esIzwUVzzk5NvL9vnkEyDhNf4fhHzUMHvyr56gNZywWTS2MR0euabMSz4A==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", + "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", + "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", + "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", + "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", + "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.5.13" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", + "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", + "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", + "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", + "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/ieee754": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", + "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "dev": true, + "requires": { + "ieee754": "^1.1.11" + } + }, + "@webassemblyjs/leb128": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", + "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "dev": true, + "requires": { + "long": "4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + } + }, + "@webassemblyjs/utf8": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", + "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", + "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/helper-wasm-section": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/wast-printer": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", + "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", + "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", + "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", + "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/floating-point-hex-parser": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-code-frame": "1.5.13", + "@webassemblyjs/helper-fsm": "1.5.13", + "long": "^3.2.0", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", + "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "long": "^3.2.0" + } + }, + "acorn": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", + "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "ajv": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz", + "integrity": "sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==", + "dev": true + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.1.tgz", + "integrity": "sha512-e1neqvSt5pSwQcFnYc6yfGuJD2Q4336cdbHs5VeUO0zTkqPbrHMyw2q1r47fpfLWbvIG8H8A6YO3sck7upTV6Q==", + "dev": true, + "requires": { + "chardet": "^0.5.0", + "iconv-lite": "^0.4.22", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules-path": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.0.tgz", + "integrity": "sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", + "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.0", + "figures": "^2.0.0", + "lodash": "^4.17.10", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.1.0", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "neo-async": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz", + "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", + "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", + "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true + }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tapable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", + "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "ts-loader": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-4.5.0.tgz", + "integrity": "sha512-ihgVaSmgrX4crGV4n7yuoHPoCHbDzj9aepCZR9TgIx4SgJ9gdnB6xLHgUBb7bsFM/f0K6x9iXa65KY/Fu1Klkw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^3.1.4", + "semver": "^5.0.1" + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", + "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", + "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "webpack": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.17.1.tgz", + "integrity": "sha512-vdPYogljzWPhFKDj3Gcp01Vqgu7K3IQlybc3XIdKSQHelK1C3eIQuysEUR7MxKJmdandZlQB/9BG2Jb1leJHaw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/wasm-edit": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" + } + }, + "webpack-cli": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.0.tgz", + "integrity": "sha512-p5NeKDtYwjZozUWq6kGNs9w+Gtw/CPvyuXjXn2HMdz8Tie+krjEg8oAtonvIyITZdvpF7XG9xDHwscLr2c+ugQ==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.0.0", + "global-modules-path": "^2.1.0", + "import-local": "^1.0.0", + "inquirer": "^6.0.0", + "interpret": "^1.1.0", + "loader-utils": "^1.1.0", + "supports-color": "^5.4.0", + "v8-compile-cache": "^2.0.0", + "yargs": "^12.0.1" + } + }, + "webpack-sources": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", + "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", + "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/package.json b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/package.json new file mode 100644 index 0000000000..7ee819c781 --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/package.json @@ -0,0 +1,19 @@ +{ + "name": "spaservices", + "version": "1.0.0", + "description": "This is not really an NPM package and will not be published. This file exists only to reference compilation tools.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack --mode production" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "^10.9.2", + "ts-loader": "^4.5.0", + "typescript": "^3.0.1", + "webpack": "^4.17.1", + "webpack-cli": "^3.1.0" + } +} diff --git a/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/webpack.config.js b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/webpack.config.js new file mode 100644 index 0000000000..e2d4c5d60a --- /dev/null +++ b/src/JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/webpack.config.js @@ -0,0 +1,31 @@ +const path = require('path'); + +module.exports = { + target: 'node', + externals: [ + // These NPM modules are loaded dynamically at runtime, rather than being bundled into the Content/Node/*.js files + // So, at runtime, they have to either be in node_modules or be built-in Node modules (e.g., 'fs') + 'aspnet-prerendering', + 'aspnet-webpack' + ], + resolve: { + extensions: [ '.ts' ] + }, + module: { + rules: [ + { test: /\.ts$/, use: 'ts-loader' }, + ] + }, + entry: { + 'prerenderer': ['./TypeScript/Prerenderer'], + 'webpack-dev-middleware': ['./TypeScript/WebpackDevMiddleware'], + }, + output: { + libraryTarget: 'commonjs', + path: path.join(__dirname, 'Content', 'Node'), + filename: '[name].js' + }, + optimization: { + minimize: false + } +}; diff --git a/src/JavaScriptServices/templates/WhereHaveTheTemplatesGone.md b/src/JavaScriptServices/templates/WhereHaveTheTemplatesGone.md new file mode 100644 index 0000000000..939f5cfe0b --- /dev/null +++ b/src/JavaScriptServices/templates/WhereHaveTheTemplatesGone.md @@ -0,0 +1,11 @@ +# Where have all the SPA templates gone? + +They are now in the [ASP.NET templating](https://github.com/aspnet/templating) repo. + +This is so that all the ASP.NET templates (both SPA and non-SPA) can be built, tested, and packaged from a single location and always included automatically in future SDK builds. + +### What about issues and pull requests? + +If you have issues or comments about the SPA templates, please continue to post them [here in this repo](https://github.com/aspnet/JavaScriptServices/issues), because then you will reach a more relevant group of contributors than if you posted to `aspnet/templating`. + +If you want to submit a pull request on the SPA templates, then of course you will neeed to submit that to the `aspnet/templating` repo, because that's where the code is now. But before doing so it's always a good idea to [post an issue on this repo](https://github.com/aspnet/JavaScriptServices/issues) describing what you want to do, so we can discuss whether or not such a PR would be accepted. diff --git a/src/JavaScriptServices/version.props b/src/JavaScriptServices/version.props new file mode 100644 index 0000000000..4889a26987 --- /dev/null +++ b/src/JavaScriptServices/version.props @@ -0,0 +1,12 @@ + + + 2.2.0 + rtm + $(VersionPrefix) + $(VersionPrefix)-$(VersionSuffix)-final + t000 + a- + $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) + $(VersionSuffix)-$(BuildNumber) + +