diff --git a/v2/NOTES.md b/v2/NOTES.md
new file mode 100644
index 000000000..16bac7122
--- /dev/null
+++ b/v2/NOTES.md
@@ -0,0 +1,40 @@
+
+# Packing linux
+
+ * create app, app.desktop, app.png (512x512)
+ * chmod +x app!
+ * ./linuxdeploy-x86_64.AppImage --appdir AppDir -i react.png -d react.desktop -e react --output appimage
+
+
+# Wails Doctor
+
+Tested on:
+
+ * Debian 8
+ * Ubuntu 20.04
+ * Ubuntu 19.10
+ * Solus 4.1
+ * Centos 8
+ * Gentoo
+ * OpenSUSE/leap
+ * Fedora 31
+
+### Development
+
+Add a new package manager processor here: `v2/internal/system/packagemanager/`. IsAvailable should work even if the package is installed.
+Add your new package manager to the list of package managers in `v2/internal/system/packagemanager/packagemanager.go`:
+
+```
+var db = map[string]PackageManager{
+ "eopkg": NewEopkg(),
+ "apt": NewApt(),
+ "yum": NewYum(),
+ "pacman": NewPacman(),
+ "emerge": NewEmerge(),
+ "zypper": NewZypper(),
+}
+```
+
+## Gentoo
+
+ * Setup docker image using: emerge-webrsync -x -v
diff --git a/v2/README.md b/v2/README.md
new file mode 100644
index 000000000..211b29696
--- /dev/null
+++ b/v2/README.md
@@ -0,0 +1,5 @@
+# Wails v2 ALPHA
+
+This branch contains WORK IN PROGRESS! There are no guarantees. Use at your peril!
+
+This document will be updated as progress is made.
\ No newline at end of file
diff --git a/v2/cmd/wails/internal/commands/build/build.go b/v2/cmd/wails/internal/commands/build/build.go
new file mode 100644
index 000000000..a578ff0b6
--- /dev/null
+++ b/v2/cmd/wails/internal/commands/build/build.go
@@ -0,0 +1,116 @@
+package build
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+
+ "github.com/leaanthony/clir"
+ "github.com/leaanthony/slicer"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/pkg/commands/build"
+)
+
+// AddBuildSubcommand adds the `build` command for the Wails application
+func AddBuildSubcommand(app *clir.Cli) {
+
+ outputType := "desktop"
+
+ validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
+
+ command := app.NewSubCommand("build", "Builds the application")
+
+ // Setup target type flag
+ description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
+ command.StringFlag("t", description, &outputType)
+
+ // Setup production flag
+ production := false
+ command.BoolFlag("production", "Build in production mode", &production)
+
+ // Setup pack flag
+ pack := false
+ command.BoolFlag("pack", "Create a platform specific package", &pack)
+
+ compilerCommand := "go"
+ command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
+
+ // Setup Platform flag
+ platform := runtime.GOOS
+ command.StringFlag("platform", "Platform to target", &platform)
+
+ // Quiet Build
+ quiet := false
+ command.BoolFlag("q", "Supress output to console", &quiet)
+
+ // ldflags to pass to `go`
+ ldflags := ""
+ command.StringFlag("ldflags", "optional ldflags", &ldflags)
+
+ // Log to file
+ logFile := ""
+ command.StringFlag("l", "Log to file", &logFile)
+
+ command.Action(func() error {
+
+ // Create logger
+ logger := logger.New()
+
+ if !quiet {
+ logger.AddOutput(os.Stdout)
+ }
+
+ // Validate output type
+ if !validTargetTypes.Contains(outputType) {
+ return fmt.Errorf("output type '%s' is not valid", outputType)
+ }
+
+ if !quiet {
+ app.PrintBanner()
+ }
+
+ task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
+ logger.Writeln(task)
+ logger.Writeln(strings.Repeat("-", len(task)))
+
+ // Setup mode
+ mode := build.Debug
+ if production {
+ mode = build.Production
+ }
+
+ // Create BuildOptions
+ buildOptions := &build.Options{
+ Logger: logger,
+ OutputType: outputType,
+ Mode: mode,
+ Pack: pack,
+ Platform: platform,
+ LDFlags: ldflags,
+ Compiler: compilerCommand,
+ }
+
+ return doBuild(buildOptions)
+ })
+}
+
+// doBuild is our main build command
+func doBuild(buildOptions *build.Options) error {
+
+ // Start Time
+ start := time.Now()
+
+ outputFilename, err := build.Build(buildOptions)
+ if err != nil {
+ return err
+ }
+ // Output stats
+ elapsed := time.Since(start)
+ buildOptions.Logger.Writeln("")
+ buildOptions.Logger.Writeln(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
+ buildOptions.Logger.Writeln("")
+
+ return nil
+}
diff --git a/v2/cmd/wails/internal/commands/dev/dev.go b/v2/cmd/wails/internal/commands/dev/dev.go
new file mode 100644
index 000000000..aeadade14
--- /dev/null
+++ b/v2/cmd/wails/internal/commands/dev/dev.go
@@ -0,0 +1,261 @@
+package dev
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+ "runtime"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/fsnotify/fsnotify"
+ "github.com/leaanthony/clir"
+ "github.com/leaanthony/slicer"
+ "github.com/wailsapp/wails/v2/internal/fs"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/process"
+ "github.com/wailsapp/wails/v2/pkg/commands/build"
+)
+
+// AddSubcommand adds the `dev` command for the Wails application
+func AddSubcommand(app *clir.Cli) error {
+
+ command := app.NewSubCommand("dev", "Development mode")
+
+ outputType := "desktop"
+
+ validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
+
+ // Setup target type flag
+ description := "Type of application to develop. Valid types: " + validTargetTypes.Join(",")
+ command.StringFlag("t", description, &outputType)
+
+ // Passthrough ldflags
+ ldflags := ""
+ command.StringFlag("ldflags", "optional ldflags", &ldflags)
+
+ // compiler command
+ compilerCommand := "go"
+ command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
+
+ // extensions to trigger rebuilds
+ extensions := "go"
+ command.StringFlag("m", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
+
+ command.Action(func() error {
+
+ // Validate inputs
+ if !validTargetTypes.Contains(outputType) {
+ return fmt.Errorf("output type '%s' is not valid", outputType)
+ }
+
+ // Create logger
+ logger := logger.New()
+ logger.AddOutput(os.Stdout)
+ app.PrintBanner()
+
+ // TODO: Check you are in a project directory
+
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ return err
+ }
+ defer watcher.Close()
+
+ var debugBinaryProcess *process.Process = nil
+ var buildFrontend bool = true
+ var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
+
+ // Setup signal handler
+ quitChannel := make(chan os.Signal, 1)
+ signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
+
+ debounceQuit := make(chan bool, 1)
+
+ // Do initial build
+ logger.Info("Building application for development...")
+ debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
+
+ go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
+ // logger.Info("event: %+v", event)
+
+ // Check for new directories
+ if event.Op&fsnotify.Create == fsnotify.Create {
+ // If this is a folder, add it to our watch list
+ if fs.DirExists(event.Name) {
+ if !strings.Contains(event.Name, "node_modules") {
+ watcher.Add(event.Name)
+ logger.Info("Watching directory: %s", event.Name)
+ }
+ }
+ return
+ }
+
+ // Check for file writes
+ if event.Op&fsnotify.Write == fsnotify.Write {
+
+ // logger.Info("modified file: %s", event.Name)
+ var rebuild bool = false
+
+ // Iterate all file patterns
+ for _, pattern := range extensionsThatTriggerARebuild {
+ rebuild = strings.HasSuffix(event.Name, pattern)
+ if err != nil {
+ logger.Fatal(err.Error())
+ }
+ if rebuild {
+ // Only build frontend when the file isn't a Go file
+ buildFrontend = !strings.HasSuffix(event.Name, "go")
+ break
+ }
+ }
+
+ if !rebuild {
+ logger.Info("Filename change: %s did not match extension list %s", event.Name, extensions)
+ return
+ }
+
+ if buildFrontend {
+ logger.Info("Full rebuild triggered: %s updated", event.Name)
+ } else {
+ logger.Info("Partial build triggered: %s updated", event.Name)
+ }
+
+ // Do a rebuild
+
+ // Try and build the app
+ newBinaryProcess := restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
+
+ // If we have a new process, save it
+ if newBinaryProcess != nil {
+ debugBinaryProcess = newBinaryProcess
+ }
+
+ }
+ })
+
+ // Get project dir
+ dir, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+
+ // Get all subdirectories
+ dirs, err := fs.GetSubdirectories(dir)
+ if err != nil {
+ return err
+ }
+
+ // Setup a watcher for non-node_modules directories
+ dirs.Each(func(dir string) {
+ if strings.Contains(dir, "node_modules") {
+ return
+ }
+ logger.Info("Watching directory: %s", dir)
+ err = watcher.Add(dir)
+ if err != nil {
+ logger.Fatal(err.Error())
+ }
+ })
+
+ // Wait until we get a quit signal
+ quit := false
+ for quit == false {
+ select {
+ case <-quitChannel:
+ println()
+ // Notify debouncer to quit
+ debounceQuit <- true
+ quit = true
+ }
+ }
+
+ // Kill the current program if running
+ if debugBinaryProcess != nil {
+ debugBinaryProcess.Kill()
+ }
+
+ logger.Info("Development mode exited")
+
+ return nil
+ })
+
+ return nil
+}
+
+// Credit: https://drailing.net/2018/01/debounce-function-for-golang/
+func debounce(interval time.Duration, input chan fsnotify.Event, quitChannel chan bool, cb func(arg fsnotify.Event)) {
+ var item fsnotify.Event
+ timer := time.NewTimer(interval)
+exit:
+ for {
+ select {
+ case item = <-input:
+ timer.Reset(interval)
+ case <-timer.C:
+ if item.Name != "" {
+ cb(item)
+ }
+ case <-quitChannel:
+ break exit
+ }
+ }
+}
+
+func restartApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
+
+ appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
+ println()
+ if err != nil {
+ logger.Error("Build Failed: %s", err.Error())
+ return nil
+ }
+ logger.Info("Build new binary: %s", appBinary)
+
+ // Kill existing binary if need be
+ if debugBinaryProcess != nil {
+ killError := debugBinaryProcess.Kill()
+
+ if killError != nil {
+ logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
+ }
+
+ debugBinaryProcess = nil
+ }
+
+ // TODO: Generate `backend.js`
+
+ // Start up new binary
+ newProcess := process.NewProcess(logger, appBinary)
+ err = newProcess.Start()
+ if err != nil {
+ // Remove binary
+ fs.DeleteFile(appBinary)
+ logger.Fatal("Unable to start application: %s", err.Error())
+ }
+
+ return newProcess
+}
+
+func buildApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
+
+ // Create random output file
+ outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
+
+ // Create BuildOptions
+ buildOptions := &build.Options{
+ Logger: logger,
+ OutputType: outputType,
+ Mode: build.Debug,
+ Pack: false,
+ Platform: runtime.GOOS,
+ LDFlags: ldflags,
+ Compiler: compilerCommand,
+ OutputFile: outputFile,
+ IgnoreFrontend: !buildFrontend,
+ }
+
+ return build.Build(buildOptions)
+
+}
diff --git a/v2/cmd/wails/internal/commands/doctor/doctor.go b/v2/cmd/wails/internal/commands/doctor/doctor.go
new file mode 100644
index 000000000..b0f6df45d
--- /dev/null
+++ b/v2/cmd/wails/internal/commands/doctor/doctor.go
@@ -0,0 +1,154 @@
+package doctor
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/leaanthony/clir"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/system"
+ "github.com/wailsapp/wails/v2/internal/system/packagemanager"
+)
+
+// AddSubcommand adds the `doctor` command for the Wails application
+func AddSubcommand(app *clir.Cli) error {
+
+ command := app.NewSubCommand("doctor", "Diagnose your environment")
+
+ command.Action(func() error {
+
+ // Create logger
+ logger := logger.New()
+ logger.AddOutput(os.Stdout)
+
+ app.PrintBanner()
+ print("Scanning system - please wait...")
+
+ // Get system info
+ info, err := system.GetInfo()
+ if err != nil {
+ return err
+ }
+ println("Done.")
+
+ // Start a new tabwriter
+ w := new(tabwriter.Writer)
+ w.Init(os.Stdout, 8, 8, 0, '\t', 0)
+
+ // Write out the system information
+ fmt.Fprintf(w, "\n")
+ fmt.Fprintf(w, "System\n")
+ fmt.Fprintf(w, "------\n")
+ fmt.Fprintf(w, "%s\t%s\n", "OS:", info.OS.Name)
+ fmt.Fprintf(w, "%s\t%s\n", "Version: ", info.OS.Version)
+ fmt.Fprintf(w, "%s\t%s\n", "ID:", info.OS.ID)
+
+ // Exit early if PM not found
+ if info.PM == nil {
+ fmt.Fprintf(w, "\n%s\t%s", "Package Manager:", "Not Found")
+ w.Flush()
+ println()
+ return nil
+ }
+ fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
+
+ // Output Go Information
+ fmt.Fprintf(w, "%s\t%s\n", "Go Version:", runtime.Version())
+ fmt.Fprintf(w, "%s\t%s\n", "Platform:", runtime.GOOS)
+ fmt.Fprintf(w, "%s\t%s\n", "Architecture:", runtime.GOARCH)
+
+ // Output Dependencies Status
+ var dependenciesMissing = []string{}
+ var externalPackages = []*packagemanager.Dependancy{}
+ var dependenciesAvailableRequired = 0
+ var dependenciesAvailableOptional = 0
+ fmt.Fprintf(w, "\n")
+ fmt.Fprintf(w, "Dependency\tPackage Name\tStatus\tVersion\n")
+ fmt.Fprintf(w, "----------\t------------\t------\t-------\n")
+
+ // Loop over dependencies
+ for _, dependency := range info.Dependencies {
+
+ name := dependency.Name
+ if dependency.Optional {
+ name += "*"
+ }
+ packageName := "Unknown"
+ status := "Not Found"
+
+ // If we found the package
+ if dependency.PackageName != "" {
+
+ packageName = dependency.PackageName
+
+ // If it's installed, update the status
+ if dependency.Installed {
+ status = "Installed"
+ } else {
+ // Generate meaningful status text
+ status = "Available"
+
+ if dependency.Optional {
+ dependenciesAvailableOptional++
+ } else {
+ dependenciesAvailableRequired++
+ }
+ }
+ } else {
+ if !dependency.Optional {
+ dependenciesMissing = append(dependenciesMissing, dependency.Name)
+ }
+
+ if dependency.External {
+ externalPackages = append(externalPackages, dependency)
+ }
+ }
+
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, packageName, status, dependency.Version)
+ }
+ fmt.Fprintf(w, "\n")
+ fmt.Fprintf(w, "* - Optional Dependency\n")
+ w.Flush()
+ println()
+ println("Diagnosis")
+ println("---------\n")
+
+ // Generate an appropriate diagnosis
+
+ if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
+ println("Your system is ready for Wails development!")
+ }
+
+ if dependenciesAvailableRequired != 0 {
+ println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
+ }
+
+ if dependenciesAvailableOptional != 0 {
+ println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
+ }
+
+ if len(externalPackages) > 0 {
+ for _, p := range externalPackages {
+ if p.Optional {
+ print("[Optional] ")
+ }
+ println("Install " + p.Name + ": " + p.InstallCommand)
+ }
+ }
+
+ if len(dependenciesMissing) != 0 {
+ // TODO: Check if apps are available locally and if so, adjust the diagnosis
+ println("Fatal:")
+ println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
+ println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
+ }
+
+ println()
+ return nil
+ })
+
+ return nil
+}
diff --git a/v2/cmd/wails/internal/commands/initialise/initialise.go b/v2/cmd/wails/internal/commands/initialise/initialise.go
new file mode 100644
index 000000000..55217aa77
--- /dev/null
+++ b/v2/cmd/wails/internal/commands/initialise/initialise.go
@@ -0,0 +1,120 @@
+package initialise
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/leaanthony/clir"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/templates"
+)
+
+// AddSubcommand adds the `init` command for the Wails application
+func AddSubcommand(app *clir.Cli) error {
+
+ // Load the template shortnames
+ validShortNames, err := templates.TemplateShortNames()
+ if err != nil {
+ return err
+ }
+
+ command := app.NewSubCommand("init", "Initialise a new Wails project")
+
+ // Setup template name flag
+ templateName := "vanilla"
+ description := "Name of template to use. Valid tempates: " + validShortNames.Join(" ")
+ command.StringFlag("t", description, &templateName)
+
+ // Setup project name
+ projectName := ""
+ command.StringFlag("n", "Name of project", &projectName)
+
+ // Setup project directory
+ projectDirectory := "."
+ command.StringFlag("d", "Project directory", &projectDirectory)
+
+ // Quiet Init
+ quiet := false
+ command.BoolFlag("q", "Supress output to console", &quiet)
+
+ // List templates
+ list := false
+ command.BoolFlag("l", "List templates", &list)
+
+ command.Action(func() error {
+
+ // Create logger
+ logger := logger.New()
+
+ if !quiet {
+ logger.AddOutput(os.Stdout)
+ }
+
+ // Are we listing templates?
+ if list {
+ app.PrintBanner()
+ err := templates.OutputList(logger)
+ logger.Writeln("")
+ return err
+ }
+
+ // Validate output type
+ if !validShortNames.Contains(templateName) {
+ logger.Write(fmt.Sprintf("ERROR: Template '%s' is not valid", templateName))
+ logger.Writeln("")
+ command.PrintHelp()
+ return nil
+ }
+
+ // Validate name
+ if len(projectName) == 0 {
+ logger.Writeln("ERROR: Project name required")
+ logger.Writeln("")
+ command.PrintHelp()
+ return nil
+ }
+
+ if !quiet {
+ app.PrintBanner()
+ }
+
+ task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
+ logger.Writeln(task)
+ logger.Writeln(strings.Repeat("-", len(task)))
+
+ // Create Template Options
+ options := &templates.Options{
+ ProjectName: projectName,
+ TargetDir: projectDirectory,
+ TemplateName: templateName,
+ Logger: logger,
+ }
+
+ return initProject(options)
+ })
+
+ return nil
+}
+
+// initProject is our main init command
+func initProject(options *templates.Options) error {
+
+ // Start Time
+ start := time.Now()
+
+ // Install the template
+ err := templates.Install(options)
+ if err != nil {
+ return err
+ }
+
+ // Output stats
+ elapsed := time.Since(start)
+ options.Logger.Writeln("")
+ options.Logger.Writeln(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
+ options.Logger.Writeln("")
+
+ return nil
+}
diff --git a/v2/cmd/wails/main.go b/v2/cmd/wails/main.go
new file mode 100644
index 000000000..d1e320a41
--- /dev/null
+++ b/v2/cmd/wails/main.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "os"
+
+ "github.com/leaanthony/clir"
+ "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
+ "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev"
+ "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
+ "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
+)
+
+func fatal(message string) {
+ println(message)
+ os.Exit(1)
+}
+
+func main() {
+
+ var err error
+ version := "v2.0.0-alpha"
+
+ app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
+
+ build.AddBuildSubcommand(app)
+ err = initialise.AddSubcommand(app)
+ if err != nil {
+ fatal(err.Error())
+ }
+ err = doctor.AddSubcommand(app)
+ if err != nil {
+ fatal(err.Error())
+ }
+
+ err = dev.AddSubcommand(app)
+ if err != nil {
+ fatal(err.Error())
+ }
+
+ err = app.Run()
+ if err != nil {
+ println("\n\nERROR: " + err.Error())
+ }
+}
diff --git a/v2/go.mod b/v2/go.mod
new file mode 100644
index 000000000..a31f361dd
--- /dev/null
+++ b/v2/go.mod
@@ -0,0 +1,18 @@
+module github.com/wailsapp/wails/v2
+
+go 1.13
+
+require (
+ github.com/fsnotify/fsnotify v1.4.9
+ github.com/leaanthony/clir v1.0.4
+ github.com/leaanthony/gosod v0.0.4
+ github.com/leaanthony/slicer v1.4.1
+ github.com/matryer/is v1.4.0
+ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
+ github.com/olekukonko/tablewriter v0.0.4
+ github.com/xyproto/xpm v1.2.1
+ golang.org/x/net v0.0.0-20200822124328-c89045814202
+ golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
+ gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
+ nhooyr.io/websocket v1.8.6
+)
diff --git a/v2/go.sum b/v2/go.sum
new file mode 100644
index 000000000..da52277bc
--- /dev/null
+++ b/v2/go.sum
@@ -0,0 +1,114 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
+github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
+github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
+github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
+github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
+github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
+github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
+github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
+github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
+github.com/leaanthony/slicer v1.4.1 h1:X/SmRIDhkUAolP79mSTO0jTcVX1k504PJBqvV6TwP0w=
+github.com/leaanthony/slicer v1.4.1/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
+github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
+github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
+github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
+github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
+golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
+nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
diff --git a/v2/internal/app/debug.go b/v2/internal/app/debug.go
new file mode 100644
index 000000000..2f3c19625
--- /dev/null
+++ b/v2/internal/app/debug.go
@@ -0,0 +1,10 @@
+// +build debug
+
+package app
+
+// Init initialises the application for a debug environment
+func (a *App) Init() error {
+ a.debug = true
+ println("Initialising debug options")
+ return nil
+}
diff --git a/v2/internal/app/default.go b/v2/internal/app/default.go
new file mode 100644
index 000000000..287a432ed
--- /dev/null
+++ b/v2/internal/app/default.go
@@ -0,0 +1,44 @@
+// +build !desktop,!hybrid,!server
+
+package app
+
+// This is the default application that will get run if the user compiles using `go build`.
+// The reason we want to prevent that is that the `wails build` command does a lot of behind
+// the scenes work such as asset compilation. If we allow `go build`, the state of these assets
+// will be unknown and the application will not work as expected.
+
+import (
+ "os"
+
+ "github.com/wailsapp/wails/v2/internal/features"
+)
+
+// App defines a Wails application structure
+type App struct {
+ Title string
+ Width int
+ Height int
+ Resizable bool
+
+ // Indicates if the app is running in debug mode
+ debug bool
+
+ Features *features.Features
+}
+
+// CreateApp returns a null application
+func CreateApp(options *Options) *App {
+ return &App{}
+}
+
+// Run the application
+func (a *App) Run() error {
+ println(`FATAL: This application was built using "go build". This is unsupported. Please compile using "wails build".`)
+ os.Exit(1)
+ return nil
+}
+
+// Bind the dummy interface
+func (a *App) Bind(dummy interface{}) error {
+ return nil
+}
diff --git a/v2/internal/app/desktop.go b/v2/internal/app/desktop.go
new file mode 100644
index 000000000..e94a504cf
--- /dev/null
+++ b/v2/internal/app/desktop.go
@@ -0,0 +1,171 @@
+// +build desktop,!server
+
+package app
+
+import (
+ "os"
+
+ "github.com/wailsapp/wails/v2/internal/binding"
+ "github.com/wailsapp/wails/v2/internal/features"
+ "github.com/wailsapp/wails/v2/internal/ffenestri"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+ "github.com/wailsapp/wails/v2/internal/signal"
+ "github.com/wailsapp/wails/v2/internal/subsystem"
+)
+
+// App defines a Wails application structure
+type App struct {
+ window *ffenestri.Application
+ servicebus *servicebus.ServiceBus
+ logger *logger.Logger
+ signal *signal.Manager
+
+ // Subsystems
+ log *subsystem.Log
+ runtime *subsystem.Runtime
+ event *subsystem.Event
+ binding *subsystem.Binding
+ call *subsystem.Call
+ dispatcher *messagedispatcher.Dispatcher
+
+ // Indicates if the app is in debug mode
+ debug bool
+
+ // This is our binding DB
+ bindings *binding.Bindings
+
+ // Feature flags
+ Features *features.Features
+}
+
+// Create App
+func CreateApp(options *Options) *App {
+
+ // Merge default options
+ options.mergeDefaults()
+
+ // Set up logger
+ myLogger := logger.New(os.Stdout)
+ myLogger.SetLogLevel(logger.TRACE)
+
+ window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
+ Title: options.Title,
+ Width: options.Width,
+ Height: options.Height,
+ MinWidth: options.MinWidth,
+ MinHeight: options.MinHeight,
+ MaxWidth: options.MaxWidth,
+ MaxHeight: options.MaxHeight,
+ Frameless: options.Frameless,
+ StartHidden: options.StartHidden,
+
+ // This should be controlled by the compile time flags...
+ DevTools: true,
+
+ Resizable: !options.DisableResize,
+ Fullscreen: options.Fullscreen,
+ }, myLogger)
+
+ result := &App{
+ window: window,
+ servicebus: servicebus.New(myLogger),
+ logger: myLogger,
+ bindings: binding.NewBindings(myLogger),
+ Features: features.New(),
+ }
+
+ // Initialise the app
+ result.Init()
+
+ return result
+
+}
+
+// Run the application
+func (a *App) Run() error {
+
+ // Setup signal handler
+ signal, err := signal.NewManager(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.signal = signal
+ a.signal.Start()
+
+ // Start the service bus
+ a.servicebus.Debug()
+ a.servicebus.Start()
+
+ // Start the runtime
+ runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.runtime = runtime
+ a.runtime.Start()
+
+ // Start the binding subsystem
+ binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
+ if err != nil {
+ return err
+ }
+ a.binding = binding
+ a.binding.Start()
+
+ // Start the logging subsystem
+ log, err := subsystem.NewLog(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.log = log
+ a.log.Start()
+
+ // create the dispatcher
+ dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.dispatcher = dispatcher
+ dispatcher.Start()
+
+ // Start the eventing subsystem
+ event, err := subsystem.NewEvent(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.event = event
+ a.event.Start()
+
+ // Start the call subsystem
+ call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
+ if err != nil {
+ return err
+ }
+ a.call = call
+ a.call.Start()
+
+ // Dump bindings as a debug
+ bindingDump, err := a.bindings.ToJSON()
+ if err != nil {
+ return err
+ }
+
+ result := a.window.Run(dispatcher, bindingDump, a.Features)
+ a.logger.Trace("Ffenestri.Run() exited")
+ a.servicebus.Stop()
+
+ return result
+}
+
+// Bind a struct to the application by passing in
+// a pointer to it
+func (a *App) Bind(structPtr interface{}) {
+
+ // Add the struct to the bindings
+ err := a.bindings.Add(structPtr)
+ if err != nil {
+ a.logger.Fatal("Error during binding: " + err.Error())
+ }
+}
diff --git a/v2/internal/app/hybrid.go b/v2/internal/app/hybrid.go
new file mode 100644
index 000000000..7d45f3f94
--- /dev/null
+++ b/v2/internal/app/hybrid.go
@@ -0,0 +1,212 @@
+// +build !server,!desktop,hybrid
+
+package app
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/leaanthony/clir"
+ "github.com/wailsapp/wails/v2/internal/binding"
+ "github.com/wailsapp/wails/v2/internal/features"
+ "github.com/wailsapp/wails/v2/internal/ffenestri"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+ "github.com/wailsapp/wails/v2/internal/subsystem"
+ "github.com/wailsapp/wails/v2/internal/webserver"
+)
+
+// Config defines the Application's configuration
+type Config struct {
+ Title string // Title is the value to be displayed in the title bar
+ Width int // Width is the desired window width
+ Height int // Height is the desired window height
+ DevTools bool // DevTools enables or disables the browser development tools
+ Resizable bool // Resizable when False prevents window resizing
+ ServerEnabled bool // ServerEnabled when True allows remote connections
+}
+
+// App defines a Wails application structure
+type App struct {
+ config Config
+ window *ffenestri.Application
+ webserver *webserver.WebServer
+ binding *subsystem.Binding
+ call *subsystem.Call
+ event *subsystem.Event
+ log *subsystem.Log
+ runtime *subsystem.Runtime
+
+ bindings *binding.Bindings
+ logger *logger.Logger
+ dispatcher *messagedispatcher.Dispatcher
+ servicebus *servicebus.ServiceBus
+
+ debug bool
+
+ // Feature flags
+ Features *features.Features
+}
+
+// Create App
+func CreateApp(options *Options) *App {
+
+ // Merge default options
+ options.mergeDefaults()
+
+ // Set up logger
+ myLogger := logger.New(os.Stdout)
+ myLogger.SetLogLevel(logger.INFO)
+
+ window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
+ Title: options.Title,
+ Width: options.Width,
+ Height: options.Height,
+ MinWidth: options.MinWidth,
+ MinHeight: options.MinHeight,
+ MaxWidth: options.MaxWidth,
+ MaxHeight: options.MaxHeight,
+ Frameless: options.Frameless,
+ StartHidden: options.StartHidden,
+
+ // This should be controlled by the compile time flags...
+ DevTools: true,
+
+ Resizable: !options.DisableResize,
+ Fullscreen: options.Fullscreen,
+ }, myLogger)
+
+ app := &App{
+ window: window,
+ webserver: webserver.NewWebServer(myLogger),
+ servicebus: servicebus.New(myLogger),
+ logger: myLogger,
+ bindings: binding.NewBindings(myLogger),
+ }
+
+ // Initialise the app
+ app.Init()
+
+ return app
+}
+
+// Run the application
+func (a *App) Run() error {
+
+ // Default app options
+ var port = 8080
+ var ip = "localhost"
+ var suppressLogging = false
+
+ // Create CLI
+ cli := clir.NewCli(filepath.Base(os.Args[0]), "Desktop/Server Build", "")
+
+ // Setup flags
+ cli.IntFlag("p", "Port to serve on", &port)
+ cli.StringFlag("i", "IP to serve on", &ip)
+ cli.BoolFlag("q", "Suppress logging", &suppressLogging)
+
+ // Setup main action
+ cli.Action(func() error {
+
+ // Set IP + Port
+ a.webserver.SetPort(port)
+ a.webserver.SetIP(ip)
+ a.webserver.SetBindings(a.bindings)
+ // Log information (if we aren't suppressing it)
+ if !suppressLogging {
+ cli.PrintBanner()
+ a.logger.Info("Running server at %s", a.webserver.URL())
+ }
+
+ a.servicebus.Start()
+ log, err := subsystem.NewLog(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.log = log
+ a.log.Start()
+ dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.dispatcher = dispatcher
+ a.dispatcher.Start()
+
+ // Start the runtime
+ runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.runtime = runtime
+ a.runtime.Start()
+
+ // Start the binding subsystem
+ binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
+ if err != nil {
+ return err
+ }
+ a.binding = binding
+ a.binding.Start()
+
+ // Start the eventing subsystem
+ event, err := subsystem.NewEvent(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.event = event
+ a.event.Start()
+
+ // Start the call subsystem
+ call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
+ if err != nil {
+ return err
+ }
+ a.call = call
+ a.call.Start()
+
+ // Required so that the WailsInit functions are fired!
+ runtime.GoRuntime().Events.Emit("wails:loaded")
+
+ // Set IP + Port
+ a.webserver.SetPort(port)
+ a.webserver.SetIP(ip)
+
+ // Log information (if we aren't suppressing it)
+ if !suppressLogging {
+ cli.PrintBanner()
+ println("Running server at " + a.webserver.URL())
+ }
+
+ // Dump bindings as a debug
+ bindingDump, err := a.bindings.ToJSON()
+ if err != nil {
+ return err
+ }
+
+ go func() {
+ if err := a.webserver.Start(dispatcher, event); err != nil {
+ a.logger.Error("Webserver failed to start %s", err)
+ }
+ }()
+
+ result := a.window.Run(dispatcher, bindingDump, a.Features)
+ a.servicebus.Stop()
+
+ return result
+ })
+
+ return cli.Run()
+}
+
+// Bind a struct to the application by passing in
+// a pointer to it
+func (a *App) Bind(structPtr interface{}) {
+
+ // Add the struct to the bindings
+ err := a.bindings.Add(structPtr)
+ if err != nil {
+ a.logger.Fatal("Error during binding: " + err.Error())
+ }
+}
diff --git a/v2/internal/app/options.go b/v2/internal/app/options.go
new file mode 100644
index 000000000..20c81f941
--- /dev/null
+++ b/v2/internal/app/options.go
@@ -0,0 +1,35 @@
+package app
+
+// Options for creating the App
+type Options struct {
+ Title string
+ Width int
+ Height int
+ DisableResize bool
+ Fullscreen bool
+ Frameless bool
+ MinWidth int
+ MinHeight int
+ MaxWidth int
+ MaxHeight int
+ StartHidden bool
+}
+
+// mergeDefaults will set the minimum default values for an application
+func (o *Options) mergeDefaults() {
+
+ // Create a default title
+ if len(o.Title) == 0 {
+ o.Title = "My Wails App"
+ }
+
+ // Default width
+ if o.Width == 0 {
+ o.Width = 1024
+ }
+
+ // Default height
+ if o.Height == 0 {
+ o.Height = 768
+ }
+}
diff --git a/v2/internal/app/production.go b/v2/internal/app/production.go
new file mode 100644
index 000000000..2916b1728
--- /dev/null
+++ b/v2/internal/app/production.go
@@ -0,0 +1,9 @@
+// +build !debug
+
+package app
+
+// Init initialises the application for a production environment
+func (a *App) Init() error {
+ println("Processing production cli options")
+ return nil
+}
diff --git a/v2/internal/app/server.go b/v2/internal/app/server.go
new file mode 100644
index 000000000..0530de58f
--- /dev/null
+++ b/v2/internal/app/server.go
@@ -0,0 +1,160 @@
+// +build server,!desktop
+
+package app
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/leaanthony/clir"
+ "github.com/wailsapp/wails/v2/internal/binding"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+ "github.com/wailsapp/wails/v2/internal/subsystem"
+ "github.com/wailsapp/wails/v2/internal/webserver"
+)
+
+// App defines a Wails application structure
+type App struct {
+ binding *subsystem.Binding
+ call *subsystem.Call
+ event *subsystem.Event
+ log *subsystem.Log
+ runtime *subsystem.Runtime
+
+ bindings *binding.Bindings
+ logger *logger.Logger
+ dispatcher *messagedispatcher.Dispatcher
+ servicebus *servicebus.ServiceBus
+ webserver *webserver.WebServer
+
+ debug bool
+}
+
+// Create App
+func CreateApp(options *Options) *App {
+ options.mergeDefaults()
+ // We ignore the inputs (for now)
+
+ // TODO: Allow logger output override on CLI
+ myLogger := logger.New(os.Stdout)
+ myLogger.SetLogLevel(logger.TRACE)
+
+ result := &App{
+ bindings: binding.NewBindings(myLogger),
+ logger: myLogger,
+ servicebus: servicebus.New(myLogger),
+ webserver: webserver.NewWebServer(myLogger),
+ }
+
+ // Initialise app
+ result.Init()
+
+ return result
+}
+
+// Run the application
+func (a *App) Run() error {
+
+ // Default app options
+ var port = 8080
+ var ip = "localhost"
+ var supressLogging = false
+ var debugMode = false
+
+ // Create CLI
+ cli := clir.NewCli(filepath.Base(os.Args[0]), "Server Build", "")
+
+ // Setup flags
+ cli.IntFlag("p", "Port to serve on", &port)
+ cli.StringFlag("i", "IP to serve on", &ip)
+ cli.BoolFlag("d", "Debug mode", &debugMode)
+ cli.BoolFlag("q", "Supress logging", &supressLogging)
+
+ // Setup main action
+ cli.Action(func() error {
+
+ // Set IP + Port
+ a.webserver.SetPort(port)
+ a.webserver.SetIP(ip)
+ a.webserver.SetBindings(a.bindings)
+ // Log information (if we aren't supressing it)
+ if !supressLogging {
+ cli.PrintBanner()
+ a.logger.Info("Running server at %s", a.webserver.URL())
+ }
+
+ if debugMode {
+ a.servicebus.Debug()
+ }
+ a.servicebus.Start()
+ log, err := subsystem.NewLog(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.log = log
+ a.log.Start()
+ dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.dispatcher = dispatcher
+ a.dispatcher.Start()
+
+ // Start the runtime
+ runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.runtime = runtime
+ a.runtime.Start()
+
+ // Start the binding subsystem
+ binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
+ if err != nil {
+ return err
+ }
+ a.binding = binding
+ a.binding.Start()
+
+ // Start the eventing subsystem
+ event, err := subsystem.NewEvent(a.servicebus, a.logger)
+ if err != nil {
+ return err
+ }
+ a.event = event
+ a.event.Start()
+
+ // Start the call subsystem
+ call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
+ if err != nil {
+ return err
+ }
+ a.call = call
+ a.call.Start()
+
+ // Required so that the WailsInit functions are fired!
+ runtime.GoRuntime().Events.Emit("wails:loaded")
+
+ if err := a.webserver.Start(dispatcher, event); err != nil {
+ a.logger.Error("Webserver failed to start %s", err)
+ return err
+ }
+
+ return nil
+ })
+
+ return cli.Run()
+}
+
+// Bind a struct to the application by passing in
+// a pointer to it
+func (a *App) Bind(structPtr interface{}) {
+
+ // Add the struct to the bindings
+ err := a.bindings.Add(structPtr)
+ if err != nil {
+ a.logger.Fatal("Error during binding: " + err.Error())
+ }
+}
diff --git a/v2/internal/assetdb/assetdb.go b/v2/internal/assetdb/assetdb.go
new file mode 100644
index 000000000..54892952e
--- /dev/null
+++ b/v2/internal/assetdb/assetdb.go
@@ -0,0 +1,112 @@
+package assetdb
+
+import (
+ "fmt"
+ "strings"
+ "unsafe"
+)
+
+// AssetDB is a database for assets encoded as byte slices
+type AssetDB struct {
+ db map[string][]byte
+}
+
+// NewAssetDB creates a new AssetDB and initialises a blank db
+func NewAssetDB() *AssetDB {
+ return &AssetDB{
+ db: make(map[string][]byte),
+ }
+}
+
+// AddAsset saves the given byte slice under the given name
+func (a *AssetDB) AddAsset(name string, data []byte) {
+ a.db[name] = data
+}
+
+// Remove removes the named asset
+func (a *AssetDB) Remove(name string) {
+ delete(a.db, name)
+}
+
+// Asset retrieves the byte slice for the given name
+func (a *AssetDB) Read(name string) ([]byte, error) {
+ result := a.db[name]
+ if result == nil {
+ return nil, fmt.Errorf("asset '%s' not found", name)
+ }
+ return result, nil
+}
+
+// AssetAsString returns the asset as a string.
+// It also returns a boolean indicating whether the asset existed or not.
+func (a *AssetDB) String(name string) (string, error) {
+ asset, err := a.Read(name)
+ if err != nil {
+ return "", err
+ }
+ return *(*string)(unsafe.Pointer(&asset)), nil
+}
+
+func (a *AssetDB) Dump() {
+ fmt.Printf("Assets:\n")
+ for k, _ := range a.db {
+ fmt.Println(k)
+ }
+}
+
+// Serialize converts the entire database to a file that when compiled will
+// reconstruct the AssetDB during init()
+// name: name of the asset.AssetDB instance
+// pkg: package name placed at the top of the file
+func (a *AssetDB) Serialize(name, pkg string) string {
+ var cdata strings.Builder
+ // Set buffer size to 4k
+ cdata.Grow(4096)
+
+ // Write header
+ header := `// DO NOT EDIT - Generated automatically
+package %s
+
+import "github.com/wailsapp/wails/v2/internal/assetdb"
+
+var (
+ %s *assetdb.AssetDB = assetdb.NewAssetDB()
+)
+
+// AssetsDB is a clean interface to the assetdb.AssetDB struct
+type AssetsDB interface {
+ Read(string) ([]byte, error)
+ String(string) (string, error)
+}
+
+// Assets returns the asset database
+func Assets() AssetsDB {
+ return %s
+}
+
+func init() {
+`
+ cdata.WriteString(fmt.Sprintf(header, pkg, name, name))
+
+ for aname, bytes := range a.db {
+ cdata.WriteString(fmt.Sprintf("\t%s.AddAsset(\"%s\", []byte{",
+ name,
+ aname))
+
+ l := len(bytes)
+ if l == 0 {
+ cdata.WriteString("0x00})\n")
+ continue
+ }
+
+ // Convert each byte to hex
+ for _, b := range bytes[:l-1] {
+ cdata.WriteString(fmt.Sprintf("0x%x, ", b))
+ }
+ cdata.WriteString(fmt.Sprintf("0x%x})\n", bytes[l-1]))
+ }
+
+ cdata.WriteString(`}`)
+
+ return cdata.String()
+}
diff --git a/v2/internal/assetdb/assetdb_test.go b/v2/internal/assetdb/assetdb_test.go
new file mode 100644
index 000000000..b3f34b9fd
--- /dev/null
+++ b/v2/internal/assetdb/assetdb_test.go
@@ -0,0 +1,70 @@
+package assetdb
+
+import "testing"
+import "github.com/matryer/is"
+
+func TestExistsAsBytes(t *testing.T) {
+
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("hello", helloworld)
+
+ result, err := db.Read("hello")
+
+ is.True(err == nil)
+ is.Equal(result, helloworld)
+}
+
+func TestNotExistsAsBytes(t *testing.T) {
+
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("hello4", helloworld)
+
+ result, err := db.Read("hello")
+
+ is.True(err != nil)
+ is.True(result == nil)
+}
+
+func TestExistsAsString(t *testing.T) {
+
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("hello", helloworld)
+
+ result, err := db.String("hello")
+
+ // Ensure it exists
+ is.True(err == nil)
+
+ // Ensure the string is the same as the byte slice
+ is.Equal(result, "Hello, World!")
+}
+
+func TestNotExistsAsString(t *testing.T) {
+
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("hello", helloworld)
+
+ result, err := db.String("help")
+
+ // Ensure it doesn't exist
+ is.True(err != nil)
+
+ // Ensure the string is blank
+ is.Equal(result, "")
+}
diff --git a/v2/internal/assetdb/filesystem.go b/v2/internal/assetdb/filesystem.go
new file mode 100644
index 000000000..48acb713f
--- /dev/null
+++ b/v2/internal/assetdb/filesystem.go
@@ -0,0 +1,176 @@
+// +build !desktop
+package assetdb
+
+import (
+ "errors"
+ "io"
+ "net/http"
+ "os"
+ "path"
+ "sort"
+ "strings"
+ "time"
+)
+
+var errWhence = errors.New("Seek: invalid whence")
+var errOffset = errors.New("Seek: invalid offset")
+
+// Open implements the http.FileSystem interface for the AssetDB
+func (a *AssetDB) Open(name string) (http.File, error) {
+ if name == "/" || name == "" {
+ return &Entry{name: "/", dir: true, db: a}, nil
+ }
+
+ if data, ok := a.db[name]; ok {
+ return &Entry{name: name, data: data, size: len(data)}, nil
+ } else {
+ for n, _ := range a.db {
+ if strings.HasPrefix(n, name+"/") {
+ return &Entry{name: name, db: a, dir: true}, nil
+ }
+ }
+ }
+ return &Entry{}, os.ErrNotExist
+}
+
+// readdir returns the directory entries for the requested directory
+func (a *AssetDB) readdir(name string) ([]os.FileInfo, error) {
+ dir := name
+ var ents []string
+
+ fim := make(map[string]os.FileInfo)
+ for fn, data := range a.db {
+ if strings.HasPrefix(fn, dir) {
+ pieces := strings.Split(fn[len(dir)+1:], "/")
+ if len(pieces) == 1 {
+ fim[pieces[0]] = FI{name: pieces[0], dir: false, size: len(data)}
+ ents = append(ents, pieces[0])
+ } else {
+ fim[pieces[0]] = FI{name: pieces[0], dir: true, size: -1}
+ ents = append(ents, pieces[0])
+ }
+ }
+ }
+
+ if len(ents) == 0 {
+ return nil, os.ErrNotExist
+ }
+
+ sort.Strings(ents)
+ var list []os.FileInfo
+ for _, dir := range ents {
+ list = append(list, fim[dir])
+ }
+ return list, nil
+}
+
+// Entry implements the http.File interface to allow for
+// use in the http.FileSystem implementation of AssetDB
+type Entry struct {
+ name string
+ data []byte
+ dir bool
+ size int
+ db *AssetDB
+ off int
+}
+
+// Close is a noop
+func (e Entry) Close() error {
+ return nil
+}
+
+// Read fills the slice provided returning how many bytes were written
+// and any errors encountered
+func (e *Entry) Read(p []byte) (n int, err error) {
+ if e.off >= e.size {
+ return 0, io.EOF
+ }
+ numout := len(p)
+ if numout > e.size {
+ numout = e.size
+ }
+ for i := 0; i < numout; i++ {
+ p[i] = e.data[e.off+i]
+ }
+ e.off += numout
+ n = int(numout)
+ err = nil
+ return
+}
+
+// Seek seeks to the specified offset from the location specified by whence
+func (e *Entry) Seek(offset int64, whence int) (int64, error) {
+ switch whence {
+ default:
+ return 0, errWhence
+ case io.SeekStart:
+ offset += 0
+ case io.SeekCurrent:
+ offset += int64(e.off)
+ case io.SeekEnd:
+ offset += int64(e.size)
+ }
+
+ if offset < 0 {
+ return 0, errOffset
+ }
+ e.off = int(offset)
+ return offset, nil
+}
+
+// Readdir returns the directory entries inside this entry if it is a directory
+func (e Entry) Readdir(count int) ([]os.FileInfo, error) {
+ ents := []os.FileInfo{}
+ if !e.dir {
+ return ents, errors.New("Not a directory")
+ }
+ return e.db.readdir(e.name)
+}
+
+// Stat returns information about this directory entry
+func (e Entry) Stat() (os.FileInfo, error) {
+ return FI{e.name, e.size, e.dir}, nil
+}
+
+// FI is the AssetDB implementation of os.FileInfo.
+type FI struct {
+ name string
+ size int
+ dir bool
+}
+
+// IsDir returns true if this is a directory
+func (fi FI) IsDir() bool {
+ return fi.dir
+}
+
+// ModTime always returns now
+func (fi FI) ModTime() time.Time {
+ return time.Time{}
+}
+
+// Mode returns the file as readonly and directories
+// as world writeable and executable
+func (fi FI) Mode() os.FileMode {
+ if fi.IsDir() {
+ return 0755 | os.ModeDir
+ }
+ return 0444
+}
+
+// Name returns the name of this object without
+// any leading slashes
+func (fi FI) Name() string {
+ return path.Base(fi.name)
+}
+
+// Size returns the size of this item
+func (fi FI) Size() int64 {
+ return int64(fi.size)
+}
+
+// Sys returns nil
+func (fi FI) Sys() interface{} {
+ return nil
+}
diff --git a/v2/internal/assetdb/filesystem_test.go b/v2/internal/assetdb/filesystem_test.go
new file mode 100644
index 000000000..1c2ed94a4
--- /dev/null
+++ b/v2/internal/assetdb/filesystem_test.go
@@ -0,0 +1,108 @@
+package assetdb
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/matryer/is"
+)
+
+func TestOpenLeadingSlash(t *testing.T) {
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("/hello", helloworld)
+
+ file, err := db.Open("/hello")
+ // Ensure it does exist
+ is.True(err == nil)
+
+ buff := make([]byte, len(helloworld))
+ n, err := file.Read(buff)
+ fmt.Printf("Error %v\n", err)
+ is.True(err == nil)
+ is.Equal(n, len(helloworld))
+ result := string(buff)
+
+ // Ensure the string is blank
+ is.Equal(result, string(helloworld))
+}
+
+func TestOpen(t *testing.T) {
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("/hello", helloworld)
+
+ file, err := db.Open("/hello")
+
+ // Ensure it does exist
+ is.True(err == nil)
+
+ buff := make([]byte, len(helloworld))
+ n, err := file.Read(buff)
+ is.True(err == nil)
+ is.Equal(n, len(helloworld))
+ result := string(buff)
+
+ // Ensure the string is blank
+ is.Equal(result, string(helloworld))
+}
+
+func TestReaddir(t *testing.T) {
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("/hello", helloworld)
+ db.AddAsset("/directory/hello", helloworld)
+ db.AddAsset("/directory/subdirectory/hello", helloworld)
+
+ dir, err := db.Open("/doesntexist")
+ is.True(err == os.ErrNotExist)
+ ents, err := dir.Readdir(-1)
+ is.Equal([]os.FileInfo{}, ents)
+
+ dir, err = db.Open("/")
+ is.True(dir != nil)
+ is.True(err == nil)
+ ents, err = dir.Readdir(-1)
+ is.True(err == nil)
+ is.Equal(3, len(ents))
+}
+
+func TestReaddirSubdirectory(t *testing.T) {
+ is := is.New(t)
+
+ var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
+
+ db := NewAssetDB()
+ db.AddAsset("/hello", helloworld)
+ db.AddAsset("/directory/hello", helloworld)
+ db.AddAsset("/directory/subdirectory/hello", helloworld)
+
+ expected := []os.FileInfo{
+ FI{name: "hello", dir: false, size: len(helloworld)},
+ FI{name: "subdirectory", dir: true, size: -1},
+ }
+
+ dir, err := db.Open("/directory")
+ is.True(dir != nil)
+ is.True(err == nil)
+ ents, err := dir.Readdir(-1)
+ is.Equal(expected, ents)
+
+ // Check sub-subdirectory
+ dir, err = db.Open("/directory/subdirectory")
+ is.True(dir != nil)
+ is.True(err == nil)
+ ents, err = dir.Readdir(-1)
+ is.True(err == nil)
+ is.Equal([]os.FileInfo{FI{name: "hello", size: len(helloworld)}}, ents)
+}
diff --git a/v2/internal/bind/bind.go b/v2/internal/bind/bind.go
new file mode 100644
index 000000000..156fd4ce4
--- /dev/null
+++ b/v2/internal/bind/bind.go
@@ -0,0 +1,9 @@
+package bind
+
+func IsStructPointer(value interface{}) bool {
+ switch t := value.(type) {
+ default:
+ println(t)
+ }
+ return false
+}
diff --git a/v2/internal/binding/binding.go b/v2/internal/binding/binding.go
new file mode 100755
index 000000000..479f964b3
--- /dev/null
+++ b/v2/internal/binding/binding.go
@@ -0,0 +1,70 @@
+package binding
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+)
+
+type Bindings struct {
+ db *DB
+ logger logger.CustomLogger
+}
+
+// NewBindings returns a new Bindings object
+func NewBindings(logger *logger.Logger) *Bindings {
+ return &Bindings{
+ db: newDB(),
+ logger: logger.CustomLogger("Bindings"),
+ }
+}
+
+// Add the given struct methods to the Bindings
+func (b *Bindings) Add(structPtr interface{}) error {
+
+ methods, err := getMethods(structPtr)
+ if err != nil {
+ return fmt.Errorf("cannout bind value to app: %s", err.Error())
+ }
+
+ for _, method := range methods {
+ splitName := strings.Split(method.Name, ".")
+ packageName := splitName[0]
+ structName := splitName[1]
+ methodName := splitName[2]
+
+ // Is this WailsInit?
+ if method.IsWailsInit() {
+ err := b.db.AddWailsInit(method)
+ if err != nil {
+ return err
+ }
+ b.logger.Trace("Registered WailsInit method: %s", method.Name)
+ continue
+ }
+
+ // Is this WailsShutdown?
+ if method.IsWailsShutdown() {
+ err := b.db.AddWailsShutdown(method)
+ if err != nil {
+ return err
+ }
+ b.logger.Trace("Registered WailsShutdown method: %s", method.Name)
+ continue
+ }
+
+ // Add it as a regular method
+ b.db.AddMethod(packageName, structName, methodName, method)
+
+ }
+ return nil
+}
+
+func (b *Bindings) DB() *DB {
+ return b.db
+}
+
+func (b *Bindings) ToJSON() (string, error) {
+ return b.db.ToJSON()
+}
diff --git a/v2/internal/binding/boundMethod.go b/v2/internal/binding/boundMethod.go
new file mode 100644
index 000000000..cdb192a53
--- /dev/null
+++ b/v2/internal/binding/boundMethod.go
@@ -0,0 +1,138 @@
+package binding
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// BoundMethod defines all the data related to a Go method that is
+// bound to the Wails application
+type BoundMethod struct {
+ Name string `json:"name"`
+ Inputs []*Parameter `json:"inputs,omitempty"`
+ Outputs []*Parameter `json:"outputs,omitempty"`
+ Comments string `json:"comments,omitempty"`
+ Method reflect.Value `json:"-"`
+}
+
+// IsWailsInit returns true if the method name is "WailsInit"
+func (b *BoundMethod) IsWailsInit() bool {
+ return strings.HasSuffix(b.Name, "WailsInit")
+}
+
+// IsWailsShutdown returns true if the method name is "WailsShutdown"
+func (b *BoundMethod) IsWailsShutdown() bool {
+ return strings.HasSuffix(b.Name, "WailsShutdown")
+}
+
+// VerifyWailsInit checks if the WailsInit signature is correct
+func (b *BoundMethod) VerifyWailsInit() error {
+ // Must only have 1 input
+ if b.InputCount() != 1 {
+ return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
+ }
+
+ // Check input type
+ if !b.Inputs[0].IsType("*goruntime.Runtime") {
+ return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
+ }
+
+ // Must only have 1 output
+ if b.OutputCount() != 1 {
+ return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
+ }
+
+ // Check output type
+ if !b.Outputs[0].IsError() {
+ return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
+ }
+
+ // Input must be of type Runtime
+ return nil
+}
+
+// VerifyWailsShutdown checks if the WailsShutdown signature is correct
+func (b *BoundMethod) VerifyWailsShutdown() error {
+ // Must have no inputs
+ if b.InputCount() != 0 {
+ return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
+ }
+
+ // Must have no outputs
+ if b.OutputCount() != 0 {
+ return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
+ }
+
+ // Input must be of type Runtime
+ return nil
+}
+
+// InputCount returns the number of inputs this bound method has
+func (b *BoundMethod) InputCount() int {
+ return len(b.Inputs)
+}
+
+// OutputCount returns the number of outputs this bound method has
+func (b *BoundMethod) OutputCount() int {
+ return len(b.Outputs)
+}
+
+// Call will attempt to call this bound method with the given args
+func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
+ // Check inputs
+ expectedInputLength := len(b.Inputs)
+ actualInputLength := len(args)
+ if expectedInputLength != actualInputLength {
+ return nil, fmt.Errorf("%s takes %d inputs. Received %d", b.Name, expectedInputLength, actualInputLength)
+ }
+
+ /** Convert inputs to reflect values **/
+
+ // Create slice for the input arguments to the method call
+ callArgs := make([]reflect.Value, expectedInputLength)
+
+ // Iterate over given arguments
+ for index, arg := range args {
+
+ // Attempt to convert the argument to the type expected by the method
+ value, err := convertArgToValue(arg, b.Inputs[index])
+
+ // If it fails, return a suitable error
+ if err != nil {
+ return nil, fmt.Errorf("%s (parameter %d): %s", b.Name, index+1, err.Error())
+ }
+
+ // Save the converted argument
+ callArgs[index] = value
+ }
+
+ // Do the call
+ callResults := b.Method.Call(callArgs)
+
+ //** Check results **//
+ var returnValue interface{}
+ var err error
+
+ switch b.OutputCount() {
+ case 1:
+ // Loop over results and determine if the result
+ // is an error or not
+ for _, result := range callResults {
+ interfac := result.Interface()
+ temp, ok := interfac.(error)
+ if ok {
+ err = temp
+ } else {
+ returnValue = interfac
+ }
+ }
+ case 2:
+ returnValue = callResults[0].Interface()
+ if temp, ok := callResults[1].Interface().(error); ok {
+ err = temp
+ }
+ }
+
+ return returnValue, err
+}
diff --git a/v2/internal/binding/db.go b/v2/internal/binding/db.go
new file mode 100644
index 000000000..e2bc6f357
--- /dev/null
+++ b/v2/internal/binding/db.go
@@ -0,0 +1,150 @@
+package binding
+
+import (
+ "encoding/json"
+ "sync"
+ "unsafe"
+)
+
+// DB is our database of method bindings
+type DB struct {
+ // map[packagename] -> map[structname] -> map[methodname]*method
+ store map[string]map[string]map[string]*BoundMethod
+
+ // This uses fully qualified method names as a shortcut for store traversal.
+ // It used for performance gains at runtime
+ methodMap map[string]*BoundMethod
+
+ // These are slices of methods registered using WailsInit and WailsShutdown
+ wailsInitMethods []*BoundMethod
+ wailsShutdownMethods []*BoundMethod
+
+ // Lock to ensure sync access to the data
+ lock sync.RWMutex
+}
+
+func newDB() *DB {
+ return &DB{
+ store: make(map[string]map[string]map[string]*BoundMethod),
+ methodMap: make(map[string]*BoundMethod),
+ }
+}
+
+// GetMethodFromStore returns the method for the given package/struct/method names
+// nil is returned if any one of those does not exist
+func (d *DB) GetMethodFromStore(packageName string, structName string, methodName string) *BoundMethod {
+
+ // Lock the db whilst processing and unlock on return
+ d.lock.RLock()
+ defer d.lock.RUnlock()
+
+ structMap, exists := d.store[packageName]
+ if !exists {
+ return nil
+ }
+ methodMap, exists := structMap[structName]
+ if !exists {
+ return nil
+ }
+ return methodMap[methodName]
+}
+
+// GetMethod returns the method for the given qualified method name
+// qualifiedMethodName is "packagename.structname.methodname"
+func (d *DB) GetMethod(qualifiedMethodName string) *BoundMethod {
+
+ // Lock the db whilst processing and unlock on return
+ d.lock.RLock()
+ defer d.lock.RUnlock()
+
+ return d.methodMap[qualifiedMethodName]
+}
+
+// AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName
+func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
+
+ // TODO: Validate inputs?
+
+ // Lock the db whilst processing and unlock on return
+ d.lock.Lock()
+ defer d.lock.Unlock()
+
+ // Get the map associated with the package name
+ structMap, exists := d.store[packageName]
+ if !exists {
+ // Create a new map for this packagename
+ d.store[packageName] = make(map[string]map[string]*BoundMethod)
+ structMap = d.store[packageName]
+ }
+
+ // Get the map associated with the struct name
+ methodMap, exists := structMap[structName]
+ if !exists {
+ // Create a new map for this packagename
+ structMap[structName] = make(map[string]*BoundMethod)
+ methodMap = structMap[structName]
+ }
+
+ // Store the method definition
+ methodMap[methodName] = methodDefinition
+
+ // Store in the methodMap
+ key := packageName + "." + structName + "." + methodName
+ d.methodMap[key] = methodDefinition
+
+}
+
+// AddWailsInit checks the given method is a WailsInit method and if it
+// is, adds it to the list of WailsInit methods
+func (d *DB) AddWailsInit(method *BoundMethod) error {
+ err := method.VerifyWailsInit()
+ if err != nil {
+ return err
+ }
+
+ // Lock the db whilst processing and unlock on return
+ d.lock.Lock()
+ defer d.lock.Unlock()
+
+ d.wailsInitMethods = append(d.wailsInitMethods, method)
+ return nil
+}
+
+// AddWailsShutdown checks the given method is a WailsInit method and if it
+// is, adds it to the list of WailsShutdown methods
+func (d *DB) AddWailsShutdown(method *BoundMethod) error {
+ err := method.VerifyWailsShutdown()
+ if err != nil {
+ return err
+ }
+
+ // Lock the db whilst processing and unlock on return
+ d.lock.Lock()
+ defer d.lock.Unlock()
+
+ d.wailsShutdownMethods = append(d.wailsShutdownMethods, method)
+ return nil
+}
+
+// ToJSON converts the method map to JSON
+func (d *DB) ToJSON() (string, error) {
+
+ // Lock the db whilst processing and unlock on return
+ d.lock.RLock()
+ defer d.lock.RUnlock()
+
+ bytes, err := json.Marshal(&d.store)
+
+ // Return zero copy string as this string will be read only
+ return *(*string)(unsafe.Pointer(&bytes)), err
+}
+
+// WailsInitMethods returns the list of registered WailsInit methods
+func (d *DB) WailsInitMethods() []*BoundMethod {
+ return d.wailsInitMethods
+}
+
+// WailsShutdownMethods returns the list of registered WailsInit methods
+func (d *DB) WailsShutdownMethods() []*BoundMethod {
+ return d.wailsShutdownMethods
+}
diff --git a/v2/internal/binding/parameter.go b/v2/internal/binding/parameter.go
new file mode 100644
index 000000000..ef10a24ec
--- /dev/null
+++ b/v2/internal/binding/parameter.go
@@ -0,0 +1,28 @@
+package binding
+
+import "reflect"
+
+// Parameter defines a Go method parameter
+type Parameter struct {
+ Name string `json:"name,omitempty"`
+ TypeName string `json:"type"`
+ reflectType reflect.Type
+}
+
+func newParameter(Name string, Type reflect.Type) *Parameter {
+ return &Parameter{
+ Name: Name,
+ TypeName: Type.String(),
+ reflectType: Type,
+ }
+}
+
+// IsType returns true if the given
+func (p *Parameter) IsType(typename string) bool {
+ return p.TypeName == typename
+}
+
+// IsError returns true if the parameter type is an error
+func (p *Parameter) IsError() bool {
+ return p.IsType("error")
+}
diff --git a/v2/internal/binding/reflect.go b/v2/internal/binding/reflect.go
new file mode 100755
index 000000000..9723d3a92
--- /dev/null
+++ b/v2/internal/binding/reflect.go
@@ -0,0 +1,120 @@
+package binding
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// isStructPtr returns true if the value given is a
+// pointer to a struct
+func isStructPtr(value interface{}) bool {
+ return reflect.ValueOf(value).Kind() == reflect.Ptr &&
+ reflect.ValueOf(value).Elem().Kind() == reflect.Struct
+}
+
+// isStructPtr returns true if the value given is a struct
+func isStruct(value interface{}) bool {
+ return reflect.ValueOf(value).Kind() == reflect.Struct
+}
+
+func getMethods(value interface{}) ([]*BoundMethod, error) {
+
+ // Create result placeholder
+ var result []*BoundMethod
+
+ // Check type
+ if !isStructPtr(value) {
+
+ if isStruct(value) {
+ name := reflect.ValueOf(value).Type().Name()
+ return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name)
+ }
+
+ return nil, fmt.Errorf("not a pointer to a struct")
+ }
+
+ // Process Struct
+ structType := reflect.TypeOf(value)
+ structValue := reflect.ValueOf(value)
+ baseName := structType.String()[1:]
+
+ // Process Methods
+ for i := 0; i < structType.NumMethod(); i++ {
+ methodDef := structType.Method(i)
+ methodName := methodDef.Name
+ fullMethodName := baseName + "." + methodName
+ method := structValue.MethodByName(methodName)
+
+ // Create new method
+ boundMethod := &BoundMethod{
+ Name: fullMethodName,
+ Inputs: nil,
+ Outputs: nil,
+ Comments: "",
+ Method: method,
+ }
+
+ // Iterate inputs
+ methodType := method.Type()
+ inputParamCount := methodType.NumIn()
+ var inputs []*Parameter
+ for inputIndex := 0; inputIndex < inputParamCount; inputIndex++ {
+ input := methodType.In(inputIndex)
+ thisParam := newParameter("", input)
+ inputs = append(inputs, thisParam)
+ }
+
+ boundMethod.Inputs = inputs
+
+ // Iterate outputs
+ outputParamCount := methodType.NumOut()
+ var outputs []*Parameter
+ for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
+ output := methodType.Out(outputIndex)
+ thisParam := newParameter("", output)
+ outputs = append(outputs, thisParam)
+ }
+ boundMethod.Outputs = outputs
+
+ // Save method in result
+ result = append(result, boundMethod)
+
+ }
+ return result, nil
+}
+
+// convertArgToValue
+func convertArgToValue(input interface{}, target *Parameter) (result reflect.Value, err error) {
+
+ // Catch type conversion panics thrown by convert
+ defer func() {
+ if r := recover(); r != nil {
+ // Modify error
+ err = fmt.Errorf("%s", r.(string)[23:])
+ }
+ }()
+
+ // Do the conversion
+
+ // Handle nil values
+ if input == nil {
+ switch target.reflectType.Kind() {
+ case reflect.Chan,
+ reflect.Func,
+ reflect.Interface,
+ reflect.Map,
+ reflect.Ptr,
+ reflect.Slice:
+ result = reflect.ValueOf(input).Convert(target.reflectType)
+ default:
+ return reflect.Zero(target.reflectType), fmt.Errorf("Unable to use null value")
+ }
+ } else {
+ result = reflect.ValueOf(input).Convert(target.reflectType)
+ }
+
+ // We don't like doing this but it's the only way to
+ // handle recover() correctly
+ return
+
+}
diff --git a/v2/internal/crypto/crypto.go b/v2/internal/crypto/crypto.go
new file mode 100644
index 000000000..60a010c3c
--- /dev/null
+++ b/v2/internal/crypto/crypto.go
@@ -0,0 +1,17 @@
+package crypto
+
+import (
+ "crypto/rand"
+ "fmt"
+ "log"
+)
+
+// RandomID returns a random ID as a string
+func RandomID() string {
+ b := make([]byte, 16)
+ _, err := rand.Read(b)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return fmt.Sprintf("%x", b)
+}
diff --git a/v2/internal/features/features.go b/v2/internal/features/features.go
new file mode 100644
index 000000000..803de53f1
--- /dev/null
+++ b/v2/internal/features/features.go
@@ -0,0 +1,13 @@
+package features
+
+// Features holds generic and platform specific feature flags
+type Features struct {
+ Linux *Linux
+}
+
+// New creates a new Features object
+func New() *Features {
+ return &Features{
+ Linux: &Linux{},
+ }
+}
diff --git a/v2/internal/features/features_linux.go b/v2/internal/features/features_linux.go
new file mode 100644
index 000000000..6e0e6676a
--- /dev/null
+++ b/v2/internal/features/features_linux.go
@@ -0,0 +1,5 @@
+package features
+
+// Linux holds linux specific feature flags
+type Linux struct {
+}
diff --git a/v2/internal/ffenestri/features_linux.go b/v2/internal/ffenestri/features_linux.go
new file mode 100644
index 000000000..1da24caed
--- /dev/null
+++ b/v2/internal/ffenestri/features_linux.go
@@ -0,0 +1,23 @@
+// +build linux
+
+package ffenestri
+
+/*
+
+#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
+#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
+
+#include
+#include "ffenestri.h"
+
+
+*/
+import "C"
+import "github.com/wailsapp/wails/v2/internal/features"
+
+func (a *Application) processOSFeatureFlags(features *features.Features) {
+
+ // Process Linux features
+ // linux := features.Linux
+
+}
diff --git a/v2/internal/ffenestri/ffenestri.go b/v2/internal/ffenestri/ffenestri.go
new file mode 100644
index 000000000..8fc1e2c59
--- /dev/null
+++ b/v2/internal/ffenestri/ffenestri.go
@@ -0,0 +1,199 @@
+package ffenestri
+
+import (
+ "runtime"
+ "strings"
+ "unsafe"
+
+ "github.com/wailsapp/wails/v2/internal/features"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher"
+)
+
+/*
+
+#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
+#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
+
+#include
+#include "ffenestri.h"
+
+
+*/
+import "C"
+
+// DEBUG is the global Ffenestri debug flag.
+// TODO: move to compile time.
+var DEBUG bool = true
+
+// Config defines how our application should be configured
+type Config struct {
+ Title string
+ Width int
+ Height int
+ MinWidth int
+ MinHeight int
+ MaxWidth int
+ MaxHeight int
+ DevTools bool
+ Resizable bool
+ Fullscreen bool
+ Frameless bool
+ StartHidden bool
+}
+
+var defaultConfig = &Config{
+ Title: "My Wails App",
+ Width: 800,
+ Height: 600,
+ DevTools: true,
+ Resizable: true,
+ Fullscreen: false,
+ Frameless: false,
+ StartHidden: false,
+}
+
+// Application is our main application object
+type Application struct {
+ config *Config
+ memory []unsafe.Pointer
+
+ // This is the main app pointer
+ app unsafe.Pointer
+
+ // Logger
+ logger logger.CustomLogger
+}
+
+func (a *Application) saveMemoryReference(mem unsafe.Pointer) {
+ a.memory = append(a.memory, mem)
+}
+
+func (a *Application) string2CString(str string) *C.char {
+ result := C.CString(str)
+ a.saveMemoryReference(unsafe.Pointer(result))
+ return result
+}
+
+func init() {
+ runtime.LockOSThread()
+}
+
+// NewApplicationWithConfig creates a new application based on the given config
+func NewApplicationWithConfig(config *Config, logger *logger.Logger) *Application {
+ return &Application{
+ config: config,
+ logger: logger.CustomLogger("Ffenestri"),
+ }
+}
+
+// NewApplication creates a new Application with the default config
+func NewApplication(logger *logger.Logger) *Application {
+ return &Application{
+ config: defaultConfig,
+ logger: logger.CustomLogger("Ffenestri"),
+ }
+}
+
+func (a *Application) freeMemory() {
+ for _, mem := range a.memory {
+ // fmt.Printf("Freeing memory: %+v\n", mem)
+ C.free(mem)
+ }
+}
+
+// bool2Cint converts a Go boolean to a C integer
+func (a *Application) bool2Cint(value bool) C.int {
+ if value {
+ return C.int(1)
+ }
+ return C.int(0)
+}
+
+// dispatcher is the interface to send messages to
+var dispatcher *messagedispatcher.DispatchClient
+
+// Dispatcher is what we register out client with
+type Dispatcher interface {
+ RegisterClient(client messagedispatcher.Client) *messagedispatcher.DispatchClient
+}
+
+// DispatchClient is the means for passing messages to the backend
+type DispatchClient interface {
+ SendMessage(string)
+}
+
+// Run the application
+func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, features *features.Features) error {
+ title := a.string2CString(a.config.Title)
+ width := C.int(a.config.Width)
+ height := C.int(a.config.Height)
+ resizable := a.bool2Cint(a.config.Resizable)
+ devtools := a.bool2Cint(a.config.DevTools)
+ fullscreen := a.bool2Cint(a.config.Fullscreen)
+ startHidden := a.bool2Cint(a.config.StartHidden)
+ app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden)
+
+ // Save app reference
+ a.app = unsafe.Pointer(app)
+
+ // Set Min Window Size
+ minWidth := C.int(a.config.MinWidth)
+ minHeight := C.int(a.config.MinHeight)
+ C.SetMinWindowSize(a.app, minWidth, minHeight)
+
+ // Set Max Window Size
+ maxWidth := C.int(a.config.MaxWidth)
+ maxHeight := C.int(a.config.MaxHeight)
+ C.SetMaxWindowSize(a.app, maxWidth, maxHeight)
+
+ // Set debug if needed
+ C.SetDebug(app, a.bool2Cint(DEBUG))
+
+ // Set Frameless
+ if a.config.Frameless {
+ C.DisableFrame(a.app)
+ }
+
+ // Escape bindings so C doesn't freak out
+ bindings = strings.ReplaceAll(bindings, `"`, `\"`)
+
+ // Set bindings
+ C.SetBindings(app, a.string2CString(bindings))
+
+ // Process feature flags
+ a.processFeatureFlags(features)
+
+ // save the dispatcher in a package variable so that the C callbacks
+ // can access it
+ dispatcher = incomingDispatcher.RegisterClient(newClient(a))
+
+ // Check we could initialise the application
+ if app != nil {
+ // Yes - Save memory reference and run app, cleaning up afterwards
+ a.saveMemoryReference(unsafe.Pointer(app))
+ C.Run(app, 0, nil)
+ } else {
+ // Oh no! We couldn't initialise the application
+ a.logger.Fatal("Cannot initialise Application.")
+ }
+
+ a.freeMemory()
+ return nil
+}
+
+// messageFromWindowCallback is called by any messages sent in
+// webkit to window.external.invoke. It relays the message on to
+// the dispatcher.
+//export messageFromWindowCallback
+func messageFromWindowCallback(data *C.char) {
+ dispatcher.DispatchMessage(C.GoString(data))
+}
+
+func (a *Application) processFeatureFlags(features *features.Features) {
+
+ // Process generic features
+
+ // Process OS Specific flags
+ a.processOSFeatureFlags(features)
+}
diff --git a/v2/internal/ffenestri/ffenestri.h b/v2/internal/ffenestri/ffenestri.h
new file mode 100644
index 000000000..ef019bc59
--- /dev/null
+++ b/v2/internal/ffenestri/ffenestri.h
@@ -0,0 +1,33 @@
+#ifndef __FFENESTRI_H__
+#define __FFENESTRI_H__
+
+#include
+
+extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden);
+extern void SetMinWindowSize(void *app, int minWidth, int minHeight);
+extern void SetMaxWindowSize(void *app, int maxWidth, int maxHeight);
+extern void Run(void *app, int argc, char **argv);
+extern void DestroyApplication(void *app);
+extern void SetDebug(void *app, int flag);
+extern void SetBindings(void *app, const char *bindings);
+extern void ExecJS(void *app, const char *script);
+extern void Hide(void *app);
+extern void Show(void *app);
+extern void Center(void *app);
+extern void Maximise(void *app);
+extern void Unmaximise(void *app);
+extern void Minimise(void *app);
+extern void Unminimise(void *app);
+extern void SetSize(void *app, int width, int height);
+extern void SetPosition(void *app, int x, int y);
+extern void Quit(void *app);
+extern void SetTitle(void *app, const char *title);
+extern void Fullscreen(void *app);
+extern void UnFullscreen(void *app);
+extern int SetColour(void *app, const char *colourString);
+extern void DisableFrame(void *app);
+extern char *SaveFileDialog(void *appPointer, char *title, char *filter);
+extern char *OpenFileDialog(void *appPointer, char *title, char *filter);
+extern char *OpenDirectoryDialog(void *appPointer, char *title, char *filter);
+
+#endif
diff --git a/v2/internal/ffenestri/ffenestri_client.go b/v2/internal/ffenestri/ffenestri_client.go
new file mode 100644
index 000000000..5465865ef
--- /dev/null
+++ b/v2/internal/ffenestri/ffenestri_client.go
@@ -0,0 +1,159 @@
+package ffenestri
+
+/*
+
+#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
+#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
+
+#include
+#include "ffenestri.h"
+
+*/
+import "C"
+
+import (
+ "strconv"
+ "unsafe"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+)
+
+// Client is our implentation of messageDispatcher.Client
+type Client struct {
+ app *Application
+ logger logger.CustomLogger
+}
+
+func newClient(app *Application) *Client {
+ return &Client{
+ app: app,
+ logger: app.logger,
+ }
+}
+
+// Quit the application
+func (c *Client) Quit() {
+ c.app.logger.Trace("Got shutdown message")
+ C.Quit(c.app.app)
+}
+
+// NotifyEvent will pass on the event message to the frontend
+func (c *Client) NotifyEvent(message string) {
+ eventMessage := `window.wails._.Notify(` + strconv.Quote(message) + `);`
+ c.app.logger.Trace("eventMessage = %+v", eventMessage)
+ C.ExecJS(c.app.app, c.app.string2CString(eventMessage))
+}
+
+// CallResult contains the result of the call from JS
+func (c *Client) CallResult(message string) {
+ callbackMessage := `window.wails._.Callback(` + strconv.Quote(message) + `);`
+ c.app.logger.Trace("callbackMessage = %+v", callbackMessage)
+ C.ExecJS(c.app.app, c.app.string2CString(callbackMessage))
+}
+
+// WindowSetTitle sets the window title to the given string
+func (c *Client) WindowSetTitle(title string) {
+ C.SetTitle(c.app.app, c.app.string2CString(title))
+}
+
+// WindowFullscreen will set the window to be fullscreen
+func (c *Client) WindowFullscreen() {
+ C.Fullscreen(c.app.app)
+}
+
+// WindowUnFullscreen will unfullscreen the window
+func (c *Client) WindowUnFullscreen() {
+ C.UnFullscreen(c.app.app)
+}
+
+// WindowShow will show the window
+func (c *Client) WindowShow() {
+ C.Show(c.app.app)
+}
+
+// WindowHide will hide the window
+func (c *Client) WindowHide() {
+ C.Hide(c.app.app)
+}
+
+// WindowCenter will hide the window
+func (c *Client) WindowCenter() {
+ C.Center(c.app.app)
+}
+
+// WindowMaximise will maximise the window
+func (c *Client) WindowMaximise() {
+ C.Maximise(c.app.app)
+}
+
+// WindowMinimise will minimise the window
+func (c *Client) WindowMinimise() {
+ C.Minimise(c.app.app)
+}
+
+// WindowUnmaximise will unmaximise the window
+func (c *Client) WindowUnmaximise() {
+ C.Unmaximise(c.app.app)
+}
+
+// WindowUnminimise will unminimise the window
+func (c *Client) WindowUnminimise() {
+ C.Unminimise(c.app.app)
+}
+
+// WindowPosition will position the window to x,y on the
+// monitor that the window is mostly on
+func (c *Client) WindowPosition(x int, y int) {
+ C.SetPosition(c.app.app, C.int(x), C.int(y))
+}
+
+// WindowSize will resize the window to the given
+// width and height
+func (c *Client) WindowSize(width int, height int) {
+ C.SetSize(c.app.app, C.int(width), C.int(height))
+}
+
+// WindowSetColour sets the window colour
+func (c *Client) WindowSetColour(colour string) bool {
+ result := C.SetColour(c.app.app, c.app.string2CString(colour))
+ return result == 1
+}
+
+// OpenFileDialog will open a file dialog with the given title
+func (c *Client) OpenFileDialog(title string, filter string) string {
+
+ cstring := C.OpenFileDialog(c.app.app, c.app.string2CString(title), c.app.string2CString(filter))
+ var result string
+ if cstring != nil {
+ result = C.GoString(cstring)
+ // Free the C string that was allocated by the dialog
+ C.free(unsafe.Pointer(cstring))
+ }
+ return result
+}
+
+// SaveFileDialog will open a save file dialog with the given title
+func (c *Client) SaveFileDialog(title string, filter string) string {
+
+ cstring := C.SaveFileDialog(c.app.app, c.app.string2CString(title), c.app.string2CString(filter))
+ var result string
+ if cstring != nil {
+ result = C.GoString(cstring)
+ // Free the C string that was allocated by the dialog
+ C.free(unsafe.Pointer(cstring))
+ }
+ return result
+}
+
+// OpenDirectoryDialog will open a directory dialog with the given title
+func (c *Client) OpenDirectoryDialog(title string, filter string) string {
+
+ cstring := C.OpenDirectoryDialog(c.app.app, c.app.string2CString(title), c.app.string2CString(filter))
+ var result string
+ if cstring != nil {
+ result = C.GoString(cstring)
+ // Free the C string that was allocated by the dialog
+ C.free(unsafe.Pointer(cstring))
+ }
+ return result
+}
diff --git a/v2/internal/ffenestri/ffenestri_linux.c b/v2/internal/ffenestri/ffenestri_linux.c
new file mode 100644
index 000000000..b06a4d681
--- /dev/null
+++ b/v2/internal/ffenestri/ffenestri_linux.c
@@ -0,0 +1,984 @@
+
+#ifndef __FFENESTRI_LINUX_H__
+#define __FFENESTRI_LINUX_H__
+
+#include "gtk/gtk.h"
+#include "webkit2/webkit2.h"
+#include
+#include
+#include
+#include
+#include
+
+// References to assets
+extern const unsigned char *assets[];
+extern const unsigned char runtime;
+extern const char *icon[];
+
+// Constants
+#define PRIMARY_MOUSE_BUTTON 1
+#define MIDDLE_MOUSE_BUTTON 2
+#define SECONDARY_MOUSE_BUTTON 3
+
+// MAIN DEBUG FLAG
+int debug;
+
+// Credit: https://stackoverflow.com/a/8465083
+char *concat(const char *s1, const char *s2)
+{
+ const size_t len1 = strlen(s1);
+ const size_t len2 = strlen(s2);
+ char *result = malloc(len1 + len2 + 1);
+ memcpy(result, s1, len1);
+ memcpy(result + len1, s2, len2 + 1);
+ return result;
+}
+
+// Debug works like sprintf but mutes if the global debug flag is true
+// Credit: https://stackoverflow.com/a/20639708
+void Debug(char *message, ...)
+{
+ if (debug)
+ {
+ char *temp = concat("TRACE | Ffenestri (C) | ", message);
+ message = concat(temp, "\n");
+ free(temp);
+ va_list args;
+ va_start(args, message);
+ vprintf(message, args);
+ free(message);
+ va_end(args);
+ }
+}
+
+extern void messageFromWindowCallback(const char *);
+typedef void (*ffenestriCallback)(const char *);
+
+struct Application
+{
+
+ // Gtk Data
+ GtkApplication *application;
+ GtkWindow *mainWindow;
+ GtkWidget *webView;
+ int signalInvoke;
+ int signalWindowDrag;
+ int signalButtonPressed;
+ int signalButtonReleased;
+ int signalLoadChanged;
+
+ // Saves the events for the drag mouse button
+ GdkEventButton *dragButtonEvent;
+
+ // The number of the default drag button
+ int dragButton;
+
+ // Window Data
+ const char *title;
+ char *id;
+ int width;
+ int height;
+ int resizable;
+ int devtools;
+ int startHidden;
+ int fullscreen;
+ int minWidth;
+ int minHeight;
+ int maxWidth;
+ int maxHeight;
+ int frame;
+
+ // User Data
+ char *HTML;
+
+ // Callback
+ ffenestriCallback sendMessageToBackend;
+
+ // Bindings
+ const char *bindings;
+
+ // Lock - used for sync operations (Should we be using g_mutex?)
+ int lock;
+};
+
+void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden)
+{
+ // Setup main application struct
+ struct Application *result = malloc(sizeof(struct Application));
+ result->title = title;
+ result->width = width;
+ result->height = height;
+ result->resizable = resizable;
+ result->devtools = devtools;
+ result->fullscreen = fullscreen;
+ result->minWidth = 0;
+ result->minHeight = 0;
+ result->maxWidth = 0;
+ result->maxHeight = 0;
+ result->frame = 1;
+ result->startHidden = startHidden;
+
+ // Default drag button is PRIMARY
+ result->dragButton = PRIMARY_MOUSE_BUTTON;
+
+ result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback;
+
+ // Create a unique ID based on the current unix timestamp
+ char temp[11];
+ sprintf(&temp[0], "%d", (int)time(NULL));
+ result->id = concat("wails.app", &temp[0]);
+
+ // Create the main GTK application
+ GApplicationFlags flags = G_APPLICATION_FLAGS_NONE;
+ result->application = gtk_application_new(result->id, flags);
+
+ // Return the application struct
+ return (void *)result;
+}
+
+void DestroyApplication(struct Application *app)
+{
+ Debug("Destroying Application");
+
+ g_application_quit(G_APPLICATION(app->application));
+
+ // Release the GTK ID string
+ if (app->id != NULL)
+ {
+ free(app->id);
+ app->id = NULL;
+ }
+ else
+ {
+ Debug("Almost a double free for app->id");
+ }
+
+ // Free the bindings
+ if (app->bindings != NULL)
+ {
+ free((void *)app->bindings);
+ app->bindings = NULL;
+ }
+ else
+ {
+ Debug("Almost a double free for app->bindings");
+ }
+
+ // Disconnect signal handlers
+ WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView);
+ g_signal_handler_disconnect(manager, app->signalInvoke);
+ if( app->frame == 0) {
+ g_signal_handler_disconnect(manager, app->signalWindowDrag);
+ g_signal_handler_disconnect(app->webView, app->signalButtonPressed);
+ g_signal_handler_disconnect(app->webView, app->signalButtonReleased);
+ }
+ g_signal_handler_disconnect(app->webView, app->signalLoadChanged);
+
+ // Release the main GTK Application
+ if (app->application != NULL)
+ {
+ g_object_unref(app->application);
+ app->application = NULL;
+ }
+ else
+ {
+ Debug("Almost a double free for app->application");
+ }
+ Debug("Finished Destroying Application");
+}
+
+// Quit will stop the gtk application and free up all the memory
+// used by the application
+void Quit(struct Application *app)
+{
+ Debug("Quit Called");
+ gtk_window_close((GtkWindow *)app->mainWindow);
+ g_application_quit((GApplication *)app->application);
+ DestroyApplication(app);
+}
+
+// SetTitle sets the main window title to the given string
+void SetTitle(struct Application *app, const char *title)
+{
+ gtk_window_set_title(app->mainWindow, title);
+}
+
+// Fullscreen sets the main window to be fullscreen
+void Fullscreen(struct Application *app)
+{
+ gtk_window_fullscreen(app->mainWindow);
+}
+
+// UnFullscreen resets the main window after a fullscreen
+void UnFullscreen(struct Application *app)
+{
+ gtk_window_unfullscreen(app->mainWindow);
+}
+
+void setMinMaxSize(struct Application *app)
+{
+ GdkGeometry size;
+ size.min_width = size.min_height = size.max_width = size.max_height = 0;
+ int flags = 0;
+ if (app->maxHeight > 0 && app->maxWidth > 0)
+ {
+ size.max_height = app->maxHeight;
+ size.max_width = app->maxWidth;
+ flags |= GDK_HINT_MAX_SIZE;
+ }
+ if (app->minHeight > 0 && app->minWidth > 0)
+ {
+ size.min_height = app->minHeight;
+ size.min_width = app->minWidth;
+ flags |= GDK_HINT_MIN_SIZE;
+ }
+ gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags);
+}
+
+char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAction, char **args) {
+ GtkFileChooserNative *native;
+ GtkFileChooserAction action = chooserAction;
+ gint res;
+ char *filename;
+
+ char *title = args[0];
+ char *filter = args[1];
+
+ native = gtk_file_chooser_native_new(title,
+ app->mainWindow,
+ action,
+ "_Open",
+ "_Cancel");
+
+ GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
+
+ // If we have filters, process them
+ if (filter[0] != '\0') {
+ GtkFileFilter *file_filter = gtk_file_filter_new();
+ gchar **filters = g_strsplit(filter, ",", -1);
+ gint i;
+ for(i = 0; filters && filters[i]; i++) {
+ gtk_file_filter_add_pattern(file_filter, filters[i]);
+ // Debug("Adding filter pattern: %s\n", filters[i]);
+ }
+ gtk_file_filter_set_name(file_filter, filter);
+ gtk_file_chooser_add_filter(chooser, file_filter);
+ g_strfreev(filters);
+ }
+
+ res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename(chooser);
+ }
+
+ g_object_unref(native);
+
+ return filename;
+}
+
+// openFileDialogInternal opens a dialog to select a file
+// NOTE: The result is a string that will need to be freed!
+char *openFileDialogInternal(struct Application *app, char **args)
+{
+ return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_OPEN, args);
+}
+
+// saveFileDialogInternal opens a dialog to select a file
+// NOTE: The result is a string that will need to be freed!
+char *saveFileDialogInternal(struct Application *app, char **args)
+{
+ return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SAVE, args);
+}
+
+
+// openDirectoryDialogInternal opens a dialog to select a directory
+// NOTE: The result is a string that will need to be freed!
+char *openDirectoryDialogInternal(struct Application *app, char **args)
+{
+ return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, args);
+}
+
+void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
+{
+ app->minWidth = minWidth;
+ app->minHeight = minHeight;
+}
+
+void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
+{
+ app->maxWidth = maxWidth;
+ app->maxHeight = maxHeight;
+}
+
+// SetColour sets the colour of the webview to the given colour string
+int SetColour(struct Application *app, const char *colourString)
+{
+ GdkRGBA rgba;
+ gboolean result = gdk_rgba_parse(&rgba, colourString);
+ if (result == FALSE)
+ {
+ return 0;
+ }
+ // Debug("Setting webview colour to: %s", colourString);
+ webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
+ return 1;
+}
+
+// DisableFrame disables the window frame
+void DisableFrame(struct Application *app)
+{
+ app->frame = 0;
+}
+
+void syncCallback(GObject *source_object,
+ GAsyncResult *res,
+ void *data)
+{
+ struct Application *app = (struct Application *)data;
+ app->lock = 0;
+}
+
+void syncEval(struct Application *app, const gchar *script)
+{
+
+ WebKitWebView *webView = (WebKitWebView *)(app->webView);
+
+ // wait for lock to free
+ while (app->lock == 1)
+ {
+ g_main_context_iteration(0, true);
+ }
+ // Set lock
+ app->lock = 1;
+
+ webkit_web_view_run_javascript(
+ webView,
+ script,
+ NULL, syncCallback, (void*)app);
+
+ while (app->lock == 1)
+ {
+ g_main_context_iteration(0, true);
+ }
+}
+
+void asyncEval(WebKitWebView *webView, const gchar *script)
+{
+ webkit_web_view_run_javascript(
+ webView,
+ script,
+ NULL, NULL, NULL);
+}
+
+typedef void (*dispatchMethod)(struct Application *app, void *);
+
+struct dispatchData
+{
+ struct Application *app;
+ dispatchMethod method;
+ void *args;
+};
+
+gboolean executeMethod(gpointer data)
+{
+ struct dispatchData *d = (struct dispatchData *)data;
+ (d->method)(d->app, d->args);
+ g_free(d);
+ return FALSE;
+}
+
+void ExecJS(struct Application *app, char *js)
+{
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)syncEval;
+ data->args = js;
+ data->app = app;
+
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+typedef char *(*dialogMethod)(struct Application *app, void *);
+
+struct dialogCall
+{
+ struct Application *app;
+ dialogMethod method;
+ void *args;
+ void *filter;
+ char *result;
+ int done;
+};
+
+gboolean executeMethodWithReturn(gpointer data)
+{
+ struct dialogCall *d = (struct dialogCall *)data;
+
+ d->result = (d->method)(d->app, d->args);
+ d->done = 1;
+ return FALSE;
+}
+
+char *OpenFileDialog(struct Application *app, char *title, char *filter)
+{
+ struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
+ data->result = NULL;
+ data->done = 0;
+ data->method = (dialogMethod)openFileDialogInternal;
+ const char* dialogArgs[]={ title, filter };
+ data->args = dialogArgs;
+ data->app = app;
+
+ gdk_threads_add_idle(executeMethodWithReturn, data);
+
+ while (data->done == 0)
+ {
+ usleep(100000);
+ }
+ g_free(data);
+ return data->result;
+}
+
+char *SaveFileDialog(struct Application *app, char *title, char *filter)
+{
+ struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
+ data->result = NULL;
+ data->done = 0;
+ data->method = (dialogMethod)saveFileDialogInternal;
+ const char* dialogArgs[]={ title, filter };
+ data->args = dialogArgs;
+ data->app = app;
+
+ gdk_threads_add_idle(executeMethodWithReturn, data);
+
+ while (data->done == 0)
+ {
+ usleep(100000);
+ }
+ Debug("Dialog done");
+ Debug("Result = %s\n", data->result);
+
+ g_free(data);
+ // Fingers crossed this wasn't freed by g_free above
+ return data->result;
+}
+
+char *OpenDirectoryDialog(struct Application *app, char *title, char *filter)
+{
+ struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
+ data->result = NULL;
+ data->done = 0;
+ data->method = (dialogMethod)openDirectoryDialogInternal;
+ const char* dialogArgs[]={ title, filter };
+ data->args = dialogArgs;
+ data->app = app;
+
+ gdk_threads_add_idle(executeMethodWithReturn, data);
+
+ while (data->done == 0)
+ {
+ usleep(100000);
+ }
+ Debug("Directory Dialog done");
+ Debug("Result = %s\n", data->result);
+ g_free(data);
+ // Fingers crossed this wasn't freed by g_free above
+ return data->result;
+}
+
+// Sets the icon to the XPM stored in icon
+void setIcon(struct Application *app)
+{
+ GdkPixbuf *appIcon = gdk_pixbuf_new_from_xpm_data((const char **)icon);
+ gtk_window_set_icon(app->mainWindow, appIcon);
+}
+
+static void load_finished_cb(WebKitWebView *webView,
+ WebKitLoadEvent load_event,
+ struct Application *app)
+{
+ switch (load_event)
+ {
+ // case WEBKIT_LOAD_STARTED:
+ // /* New load, we have now a provisional URI */
+ // // printf("Start downloading %s\n", webkit_web_view_get_uri(web_view));
+ // /* Here we could start a spinner or update the
+ // * location bar with the provisional URI */
+ // break;
+ // case WEBKIT_LOAD_REDIRECTED:
+ // // printf("Redirected to: %s\n", webkit_web_view_get_uri(web_view));
+ // break;
+ // case WEBKIT_LOAD_COMMITTED:
+ // /* The load is being performed. Current URI is
+ // * the final one and it won't change unless a new
+ // * load is requested or a navigation within the
+ // * same page is performed */
+ // // printf("Loading: %s\n", webkit_web_view_get_uri(web_view));
+ // break;
+ case WEBKIT_LOAD_FINISHED:
+ /* Load finished, we can now stop the spinner */
+ // printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view));
+
+ // Bindings
+ Debug("Binding Methods");
+ syncEval(app, app->bindings);
+
+ // Runtime
+ Debug("Setting up Wails runtime");
+ syncEval(app, &runtime);
+
+ // Loop over assets
+ int index = 1;
+ while (1)
+ {
+ // Get next asset pointer
+ const char *asset = assets[index];
+
+ // If we have no more assets, break
+ if (asset == 0x00)
+ {
+ break;
+ }
+
+ // sync eval the asset
+ syncEval(app, asset);
+ index++;
+ };
+
+ // Set the icon
+ setIcon(app);
+
+ // Setup fullscreen
+ if (app->fullscreen)
+ {
+ Debug("Going fullscreen");
+ Fullscreen(app);
+ }
+
+ // Setup resize
+ gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height);
+
+ if (app->resizable)
+ {
+ gtk_window_set_default_size(GTK_WINDOW(app->mainWindow), app->width, app->height);
+ }
+ else
+ {
+ gtk_widget_set_size_request(GTK_WIDGET(app->mainWindow), app->width, app->height);
+ gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height);
+ // Fix the min/max to the window size for good measure
+ app->minHeight = app->maxHeight = app->height;
+ app->minWidth = app->maxWidth = app->width;
+ }
+ gtk_window_set_resizable(GTK_WINDOW(app->mainWindow), app->resizable ? TRUE : FALSE);
+ setMinMaxSize(app);
+
+ // Centre by default
+ gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER);
+
+ // Show window and focus
+ if( app->startHidden == 0) {
+ gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
+ gtk_widget_grab_focus(app->webView);
+ }
+ break;
+ }
+}
+
+static gboolean disable_context_menu_cb(
+ WebKitWebView *web_view,
+ WebKitContextMenu *context_menu,
+ GdkEvent *event,
+ WebKitHitTestResult *hit_test_result,
+ gpointer user_data)
+{
+ return TRUE;
+}
+
+static void printEvent(const char *message, GdkEventButton *event)
+{
+ Debug("%s: [button:%d] [x:%f] [y:%f] [time:%d]",
+ message,
+ event->button,
+ event->x_root,
+ event->y_root,
+ event->time);
+}
+
+
+static void dragWindow(WebKitUserContentManager *contentManager,
+ WebKitJavascriptResult *result,
+ struct Application *app)
+{
+ // If we get this message erroneously, ignore
+ if (app->dragButtonEvent == NULL)
+ {
+ return;
+ }
+
+ // Ignore non-toplevel widgets
+ GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(app->webView));
+ if (!GTK_IS_WINDOW(window))
+ {
+ return;
+ }
+
+ // Initiate the drag
+ printEvent("Starting drag with event", app->dragButtonEvent);
+
+ gtk_window_begin_move_drag(app->mainWindow,
+ app->dragButton,
+ app->dragButtonEvent->x_root,
+ app->dragButtonEvent->y_root,
+ app->dragButtonEvent->time);
+ // Clear the event
+ app->dragButtonEvent = NULL;
+
+ return;
+}
+
+gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, struct Application *app)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton)
+ {
+ app->dragButtonEvent = event;
+ }
+ return FALSE;
+}
+
+gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Application *app)
+{
+ if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton)
+ {
+ app->dragButtonEvent = NULL;
+ }
+ return FALSE;
+}
+
+static void sendMessageToBackend(WebKitUserContentManager *contentManager,
+ WebKitJavascriptResult *result,
+ struct Application *app)
+{
+#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
+ JSCValue *value = webkit_javascript_result_get_js_value(result);
+ char *message = jsc_value_to_string(value);
+#else
+ JSGlobalContextRef context = webkit_javascript_result_get_global_context(result);
+ JSValueRef value = webkit_javascript_result_get_value(result);
+ JSStringRef js = JSValueToStringCopy(context, value, NULL);
+ size_t messageSize = JSStringGetMaximumUTF8CStringSize(js);
+ char *message = g_new(char, messageSize);
+ JSStringGetUTF8CString(js, message, messageSize);
+ JSStringRelease(js);
+#endif
+ app->sendMessageToBackend(message);
+ g_free(message);
+}
+
+void SetDebug(struct Application *app, int flag)
+{
+ debug = flag;
+}
+
+// getCurrentMonitorGeometry gets the geometry of the monitor
+// that the window is mostly on.
+GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) {
+ // Get the monitor that the window is currently on
+ GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window));
+ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+ GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, gdk_window);
+
+ // Get the geometry of the monitor
+ GdkRectangle result;
+ gdk_monitor_get_geometry (monitor,&result);
+
+ return result;
+}
+
+/*******************
+ * Window Position *
+ *******************/
+
+// Position holds an x/y corrdinate
+struct Position {
+ int x;
+ int y;
+};
+
+// Internal call for setting the position of the window.
+void setPositionInternal(struct Application *app, struct Position *pos) {
+
+ // Get the monitor geometry
+ GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
+
+ // Move the window relative to the monitor
+ gtk_window_move(app->mainWindow, m.x + pos->x, m.y + pos->y);
+
+ // Free memory
+ free(pos);
+}
+
+// SetPosition sets the position of the window to the given x/y
+// coordinates. The x/y values are relative to the monitor
+// the window is mostly on.
+void SetPosition(struct Application *app, int x, int y) {
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)setPositionInternal;
+ struct Position *pos = malloc(sizeof(struct Position));
+ pos->x = x;
+ pos->y = y;
+ data->args = pos;
+ data->app = app;
+
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+/***************
+ * Window Size *
+ ***************/
+
+// Size holds a width/height
+struct Size {
+ int width;
+ int height;
+};
+
+// Internal call for setting the size of the window.
+void setSizeInternal(struct Application *app, struct Size *size) {
+ gtk_window_resize(app->mainWindow, size->width, size->height);
+ free(size);
+}
+
+// SetSize sets the size of the window to the given width/height
+void SetSize(struct Application *app, int width, int height) {
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)setSizeInternal;
+ struct Size *size = malloc(sizeof(struct Size));
+ size->width = width;
+ size->height = height;
+ data->args = size;
+ data->app = app;
+
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+
+// centerInternal will center the main window on the monitor it is mostly in
+void centerInternal(struct Application *app)
+{
+ // Get the geometry of the monitor
+ GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
+
+ // Get the window width/height
+ int windowWidth, windowHeight;
+ gtk_window_get_size(app->mainWindow, &windowWidth, &windowHeight);
+
+ // Place the window at the center of the monitor
+ gtk_window_move(app->mainWindow, ((m.width - windowWidth) / 2) + m.x, ((m.height - windowHeight) / 2) + m.y);
+}
+
+// Center the window
+void Center(struct Application *app) {
+
+ // Setup a call to centerInternal on the main thread
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)centerInternal;
+ data->app = app;
+
+ // Add call to main thread
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+// hideInternal hides the main window
+void hideInternal(struct Application *app) {
+ gtk_widget_hide (GTK_WIDGET(app->mainWindow));
+}
+
+// Hide places the hideInternal method onto the main thread for execution
+void Hide(struct Application *app) {
+
+ // Setup a call to hideInternal on the main thread
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)hideInternal;
+ data->app = app;
+
+ // Add call to main thread
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+// showInternal shows the main window
+void showInternal(struct Application *app) {
+ gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
+ gtk_widget_grab_focus(app->webView);
+}
+
+// Show places the showInternal method onto the main thread for execution
+void Show(struct Application *app) {
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)showInternal;
+ data->app = app;
+
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+
+// maximiseInternal maximises the main window
+void maximiseInternal(struct Application *app) {
+ gtk_window_maximize(GTK_WIDGET(app->mainWindow));
+}
+
+// Maximise places the maximiseInternal method onto the main thread for execution
+void Maximise(struct Application *app) {
+
+ // Setup a call to maximiseInternal on the main thread
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)maximiseInternal;
+ data->app = app;
+
+ // Add call to main thread
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+// unmaximiseInternal unmaximises the main window
+void unmaximiseInternal(struct Application *app) {
+ gtk_window_unmaximize(GTK_WIDGET(app->mainWindow));
+}
+
+// Unmaximise places the unmaximiseInternal method onto the main thread for execution
+void Unmaximise(struct Application *app) {
+
+ // Setup a call to unmaximiseInternal on the main thread
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)unmaximiseInternal;
+ data->app = app;
+
+ // Add call to main thread
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+
+// minimiseInternal minimises the main window
+void minimiseInternal(struct Application *app) {
+ gtk_window_iconify(app->mainWindow);
+}
+
+// Minimise places the minimiseInternal method onto the main thread for execution
+void Minimise(struct Application *app) {
+
+ // Setup a call to minimiseInternal on the main thread
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)minimiseInternal;
+ data->app = app;
+
+ // Add call to main thread
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+// unminimiseInternal unminimises the main window
+void unminimiseInternal(struct Application *app) {
+ gtk_window_present(app->mainWindow);
+}
+
+// Unminimise places the unminimiseInternal method onto the main thread for execution
+void Unminimise(struct Application *app) {
+
+ // Setup a call to unminimiseInternal on the main thread
+ struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
+ data->method = (dispatchMethod)unminimiseInternal;
+ data->app = app;
+
+ // Add call to main thread
+ gdk_threads_add_idle(executeMethod, data);
+}
+
+
+void SetBindings(struct Application *app, const char *bindings)
+{
+ const char *temp = concat("window.wailsbindings = \"", bindings);
+ const char *jscall = concat(temp, "\";");
+ free((void *)temp);
+ app->bindings = jscall;
+}
+
+// This is called when the close button on the window is pressed
+gboolean close_button_pressed(GtkWidget *widget,
+ GdkEvent *event,
+ struct Application *app)
+{
+ app->sendMessageToBackend("WC"); // Window Close
+ return TRUE;
+}
+
+static void setupWindow(struct Application *app)
+{
+
+ // Create the window
+ GtkWidget *mainWindow = gtk_application_window_new(app->application);
+ // Save reference
+ app->mainWindow = GTK_WINDOW(mainWindow);
+
+ // Setup frame
+ gtk_window_set_decorated((GtkWindow *)mainWindow, app->frame);
+
+ // Setup title
+ gtk_window_set_title(GTK_WINDOW(mainWindow), app->title);
+
+ // Setup script handler
+ WebKitUserContentManager *contentManager = webkit_user_content_manager_new();
+
+ // Setup the invoke handler
+ webkit_user_content_manager_register_script_message_handler(contentManager, "external");
+ app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app);
+
+ // Setup the window drag handler if this is a frameless app
+ if ( app->frame == 0 ) {
+ webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag");
+ app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app);
+ // Setup the mouse handlers
+ app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app);
+ app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app);
+ }
+ GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager);
+
+ // Save reference
+ app->webView = webView;
+
+ // Add the webview to the window
+ gtk_container_add(GTK_CONTAINER(mainWindow), webView);
+
+
+ // Load default HTML
+ app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app);
+
+ // Load the user's HTML
+ // assets[0] is the HTML because the asset array is bundled like that by convention
+ webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webView), assets[0]);
+
+ // Check if we want to enable the dev tools
+ if (app->devtools)
+ {
+ WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView));
+ // webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
+ webkit_settings_set_enable_developer_extras(settings, true);
+ }
+ else
+ {
+ g_signal_connect(G_OBJECT(webView), "context-menu", G_CALLBACK(disable_context_menu_cb), app);
+ }
+
+ // Listen for close button signal
+ g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app);
+}
+
+static void activate(GtkApplication* _, struct Application *app)
+{
+ setupWindow(app);
+}
+
+void Run(struct Application *app, int argc, char **argv)
+{
+ g_signal_connect(app->application, "activate", G_CALLBACK(activate), app);
+ g_application_run(G_APPLICATION(app->application), argc, argv);
+}
+
+#endif
diff --git a/v2/internal/fs/fs.go b/v2/internal/fs/fs.go
new file mode 100644
index 000000000..a0b33d6cc
--- /dev/null
+++ b/v2/internal/fs/fs.go
@@ -0,0 +1,171 @@
+package fs
+
+import (
+ "crypto/md5"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "unsafe"
+
+ "github.com/leaanthony/slicer"
+)
+
+// LocalDirectory gets the caller's file directory
+// Equivalent to node's __DIRNAME
+func LocalDirectory() string {
+ _, thisFile, _, _ := runtime.Caller(1)
+ return filepath.Dir(thisFile)
+}
+
+// Mkdir will create the given directory
+func Mkdir(dirname string) error {
+ return os.Mkdir(dirname, 0755)
+}
+
+// DeleteFile will delete the given file
+func DeleteFile(filename string) error {
+ return os.Remove(filename)
+}
+
+// CopyFile from source to target
+func CopyFile(source string, target string) error {
+ s, err := os.Open(source)
+ if err != nil {
+ return err
+ }
+ defer s.Close()
+ d, err := os.Create(target)
+ if err != nil {
+ return err
+ }
+ if _, err := io.Copy(d, s); err != nil {
+ d.Close()
+ return err
+ }
+ return d.Close()
+}
+
+// DirExists - Returns true if the given path resolves to a directory on the filesystem
+func DirExists(path string) bool {
+ fi, err := os.Lstat(path)
+ if err != nil {
+ return false
+ }
+
+ return fi.Mode().IsDir()
+}
+
+// FileExists returns a boolean value indicating whether
+// the given file exists
+func FileExists(path string) bool {
+ fi, err := os.Lstat(path)
+ if err != nil {
+ return false
+ }
+
+ return fi.Mode().IsRegular()
+}
+
+// RelativePath returns a qualified path created by joining the
+// directory of the calling file and the given relative path.
+//
+// Example: RelativePath("..") in *this* file would give you '/path/to/wails2/v2/internal`
+func RelativePath(relativepath string, optionalpaths ...string) string {
+ _, thisFile, _, _ := runtime.Caller(1)
+ localDir := filepath.Dir(thisFile)
+
+ // If we have optional paths, join them to the relativepath
+ if len(optionalpaths) > 0 {
+ paths := []string{relativepath}
+ paths = append(paths, optionalpaths...)
+ relativepath = filepath.Join(paths...)
+ }
+ result, err := filepath.Abs(filepath.Join(localDir, relativepath))
+ if err != nil {
+ // I'm allowing this for 1 reason only: It's fatal if the path
+ // supplied is wrong as it's only used internally in Wails. If we get
+ // that path wrong, we should know about it immediately. The other reason is
+ // that it cuts down a ton of unnecassary error handling.
+ panic(err)
+ }
+ return result
+}
+
+// MustLoadString attempts to load a string and will abort with a fatal message if
+// something goes wrong
+func MustLoadString(filename string) string {
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ fmt.Printf("FATAL: Unable to load file '%s': %s\n", filename, err.Error())
+ os.Exit(1)
+ }
+ return *(*string)(unsafe.Pointer(&data))
+}
+
+// MD5File returns the md5sum of the given file
+func MD5File(filename string) (string, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ h := md5.New()
+ if _, err := io.Copy(h, f); err != nil {
+ return "", err
+ }
+
+ return fmt.Sprintf("%x", h.Sum(nil)), nil
+}
+
+// MustMD5File will call MD5File and abort the program on error
+func MustMD5File(filename string) string {
+ result, err := MD5File(filename)
+ if err != nil {
+ println("FATAL: Unable to MD5Sum file:", err.Error())
+ os.Exit(1)
+ }
+ return result
+}
+
+// MustWriteString will attempt to write the given data to the given filename
+// It will abort the program in the event of a failure
+func MustWriteString(filename string, data string) {
+ err := ioutil.WriteFile(filename, []byte(data), 0755)
+ if err != nil {
+ fatal("Unable to write file", filename, ":", err.Error())
+ os.Exit(1)
+ }
+}
+
+// fatal will print the optional messages and die
+func fatal(message ...string) {
+ if len(message) > 0 {
+ print("FATAL:")
+ for text := range message {
+ print(text)
+ }
+ }
+ os.Exit(1)
+}
+
+// GetSubdirectories returns a list of subdirectories for the given root directory
+func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
+ var result slicer.StringSlicer
+
+ // Iterate root dir
+ err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ // If we have a directory, save it
+ if info.IsDir() {
+ result.Add(path)
+ }
+ return nil
+ })
+ return &result, err
+}
diff --git a/v2/internal/fs/fs_test.go b/v2/internal/fs/fs_test.go
new file mode 100644
index 000000000..2cc524123
--- /dev/null
+++ b/v2/internal/fs/fs_test.go
@@ -0,0 +1,31 @@
+package fs
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/matryer/is"
+)
+
+func TestRelativePath(t *testing.T) {
+
+ is := is.New(t)
+
+ cwd, err := os.Getwd()
+ is.Equal(err, nil)
+
+ // Check current directory
+ actual := RelativePath(".")
+ is.Equal(actual, cwd)
+
+ // Check 2 parameters
+ actual = RelativePath("..", "fs")
+ is.Equal(actual, cwd)
+
+ // Check 3 parameters including filename
+ actual = RelativePath("..", "fs", "fs.go")
+ expected := filepath.Join(cwd, "fs.go")
+ is.Equal(actual, expected)
+
+}
diff --git a/v2/internal/html/asset.go b/v2/internal/html/asset.go
new file mode 100644
index 000000000..fa1a5eae2
--- /dev/null
+++ b/v2/internal/html/asset.go
@@ -0,0 +1,108 @@
+package html
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/url"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "unsafe"
+)
+
+type assetTypes struct {
+ JS string
+ CSS string
+ FAVICON string
+ HTML string
+}
+
+// AssetTypes is an enum for the asset type keys
+var AssetTypes *assetTypes = &assetTypes{
+ JS: "javascript",
+ CSS: "css",
+ FAVICON: "favicon",
+ HTML: "html",
+}
+
+// Asset describes an asset type and its path
+type Asset struct {
+ Type string
+ Path string
+ Data string
+}
+
+// Load the asset from disk
+func (a *Asset) Load(basedirectory string) error {
+ assetpath := filepath.Join(basedirectory, a.Path)
+ data, err := ioutil.ReadFile(assetpath)
+ if err != nil {
+ return err
+ }
+ a.Data = string(data)
+ return nil
+}
+
+// AsString returns the data as a READ ONLY string
+func (a *Asset) AsString() string {
+ return a.Data
+}
+
+// AsCHexData processes the asset data so it may be used by C
+func (a *Asset) AsCHexData() string {
+
+ // This will be our final string to hexify
+ dataString := a.Data
+
+ switch a.Type {
+ case AssetTypes.HTML:
+
+ // Escape HTML
+ var re = regexp.MustCompile(`\s{2,}`)
+ result := re.ReplaceAllString(a.Data, ``)
+ result = strings.ReplaceAll(result, "\n", "")
+ result = strings.ReplaceAll(result, "\r\n", "")
+ result = strings.ReplaceAll(result, "\n", "")
+ url := url.URL{Path: result}
+ urlString := strings.ReplaceAll(url.String(), "/", "%2f")
+
+ // Save Data uRI string
+ dataString = "data:text/html;charset=utf-8," + urlString
+
+ case AssetTypes.CSS:
+
+ // Escape CSS data
+ var re = regexp.MustCompile(`\s{2,}`)
+ result := re.ReplaceAllString(a.Data, ``)
+ result = strings.ReplaceAll(result, "\n", "")
+ result = strings.ReplaceAll(result, "\r\n", "")
+ result = strings.ReplaceAll(result, "\n", "")
+ result = strings.ReplaceAll(result, "\t", "")
+ result = strings.ReplaceAll(result, `\`, `\\`)
+ result = strings.ReplaceAll(result, `"`, `\"`)
+ result = strings.ReplaceAll(result, `'`, `\'`)
+ result = strings.ReplaceAll(result, ` {`, `{`)
+ result = strings.ReplaceAll(result, `: `, `:`)
+ dataString = fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result)
+ }
+
+ // Get byte data of the string
+ bytes := *(*[]byte)(unsafe.Pointer(&dataString))
+
+ // Create a strings builder
+ var cdata strings.Builder
+
+ // Set buffer size to 4k
+ cdata.Grow(4096)
+
+ // Convert each byte to hex
+ for _, b := range bytes {
+ cdata.WriteString(fmt.Sprintf("0x%x, ", b))
+ }
+
+ return cdata.String()
+}
+
+func (a *Asset) Dump() {
+ fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
+}
diff --git a/v2/internal/html/assetbundle.go b/v2/internal/html/assetbundle.go
new file mode 100644
index 000000000..1d60f3d81
--- /dev/null
+++ b/v2/internal/html/assetbundle.go
@@ -0,0 +1,195 @@
+package html
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "path/filepath"
+ "strings"
+
+ "github.com/leaanthony/slicer"
+ "github.com/wailsapp/wails/v2/internal/assetdb"
+ "golang.org/x/net/html"
+)
+
+// AssetBundle is a collection of Assets
+type AssetBundle struct {
+ assets []*Asset
+ basedirectory string
+}
+
+// NewAssetBundle creates a new AssetBundle struct containing
+// the given html and all the assets referenced by it
+func NewAssetBundle(pathToHTML string) (*AssetBundle, error) {
+
+ // Create result
+ result := &AssetBundle{
+ basedirectory: filepath.Dir(pathToHTML),
+ }
+
+ err := result.loadAssets(pathToHTML)
+ if err != nil {
+ return nil, err
+ }
+
+ return result, nil
+}
+
+// loadAssets processes the given html file and loads in
+// all referenced assets
+func (a *AssetBundle) loadAssets(pathToHTML string) error {
+
+ // Save HTML
+ htmlAsset := &Asset{
+ Type: AssetTypes.HTML,
+ Path: filepath.Base(pathToHTML),
+ }
+ err := htmlAsset.Load(a.basedirectory)
+ if err != nil {
+ return err
+ }
+ a.assets = append(a.assets, htmlAsset)
+
+ return a.processHTML(htmlAsset.AsString())
+}
+
+// Credit to: https://drstearns.github.io/tutorials/tokenizing/
+func (a *AssetBundle) processHTML(htmldata string) error {
+
+ // Tokenize the html
+ buf := bytes.NewBufferString(htmldata)
+ tokenizer := html.NewTokenizer(buf)
+
+ for {
+ //get the next token type
+ tokenType := tokenizer.Next()
+
+ //if it's an error token, we either reached
+ //the end of the file, or the HTML was malformed
+ if tokenType == html.ErrorToken {
+ err := tokenizer.Err()
+ if err == io.EOF {
+ //end of the file, break out of the loop
+ break
+ }
+ //otherwise, there was an error tokenizing,
+ //which likely means the HTML was malformed.
+ //since this is a simple command-line utility,
+ //we can just use log.Fatalf() to report the error
+ //and exit the process with a non-zero status code
+ return tokenizer.Err()
+ }
+
+ //process the token according to the token type...
+ if tokenType == html.StartTagToken {
+ //get the token
+ token := tokenizer.Token()
+
+ //if the name of the element is "title"
+ if "link" == token.Data {
+ //the next token should be the page title
+ tokenType = tokenizer.Next()
+ //just make sure it's actually a text token
+ asset := &Asset{}
+ for _, attr := range token.Attr {
+ // Favicon
+ if attr.Key == "rel" && attr.Val == "icon" {
+ asset.Type = AssetTypes.FAVICON
+ }
+ if attr.Key == "href" {
+ asset.Path = attr.Val
+ }
+ // stylesheet
+ if attr.Key == "rel" && attr.Val == "stylesheet" {
+ asset.Type = AssetTypes.CSS
+ }
+ }
+ err := asset.Load(a.basedirectory)
+ if err != nil {
+ return err
+ }
+ a.assets = append(a.assets, asset)
+ }
+ if "script" == token.Data {
+
+ tokenType = tokenizer.Next()
+ //just make sure it's actually a text token
+ asset := &Asset{Type: AssetTypes.JS}
+ for _, attr := range token.Attr {
+ if attr.Key == "src" {
+ asset.Path = attr.Val
+ break
+ }
+ }
+ err := asset.Load(a.basedirectory)
+ if err != nil {
+ return err
+ }
+ a.assets = append(a.assets, asset)
+ }
+ }
+ }
+
+ return nil
+}
+
+// WriteToCFile dumps all the assets to C files in the given directory
+func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
+
+ // Write out the assets.c file
+ var cdata strings.Builder
+
+ // Write header
+ header := `// assets.c
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL.
+// This file was auto-generated. DO NOT MODIFY.
+
+`
+ cdata.WriteString(header)
+
+ // Loop over the Assets
+ var err error
+ assetVariables := slicer.String()
+ var variableName string
+ for index, asset := range a.assets {
+ variableName = fmt.Sprintf("%s%d", asset.Type, index)
+ assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
+ cdata.WriteString(assetCdata)
+ assetVariables.Add(variableName)
+ }
+
+ if assetVariables.Length() > 0 {
+ cdata.WriteString(fmt.Sprintf("\nconst char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
+ } else {
+ cdata.WriteString("\nconst char *assets[] = { 0x00 };")
+ }
+
+ // Save file
+ assetsFile := filepath.Join(targetDir, "assets.c")
+ err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600)
+ if err != nil {
+ return "", err
+ }
+ return assetsFile, nil
+}
+
+// ConvertToAssetDB returns an assetdb.AssetDB initialized with
+// the items in the AssetBundle
+func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
+ assetdb := assetdb.NewAssetDB()
+
+ // Loop over the Assets
+ for _, asset := range a.assets {
+ assetdb.AddAsset(asset.Path, []byte(asset.Data))
+ }
+
+ return assetdb, nil
+}
+
+func (a *AssetBundle) Dump() {
+ println("Assets:")
+ for _, asset := range a.assets {
+ asset.Dump()
+ }
+}
diff --git a/v2/internal/logger/custom_logger.go b/v2/internal/logger/custom_logger.go
new file mode 100644
index 000000000..cfdb906b9
--- /dev/null
+++ b/v2/internal/logger/custom_logger.go
@@ -0,0 +1,98 @@
+package logger
+
+import (
+ "fmt"
+ "os"
+)
+
+// CustomLogger defines what a user can do with a logger
+type CustomLogger interface {
+ // Writeln writes directly to the output with no log level plus line ending
+ Writeln(message string) error
+
+ // Write writes directly to the output with no log level
+ Write(message string) error
+
+ // Trace level logging. Works like Sprintf.
+ Trace(format string, args ...interface{}) error
+
+ // Debug level logging. Works like Sprintf.
+ Debug(format string, args ...interface{}) error
+
+ // Info level logging. Works like Sprintf.
+ Info(format string, args ...interface{}) error
+
+ // Warning level logging. Works like Sprintf.
+ Warning(format string, args ...interface{}) error
+
+ // Error level logging. Works like Sprintf.
+ Error(format string, args ...interface{}) error
+
+ // Fatal level logging. Works like Sprintf.
+ Fatal(format string, args ...interface{})
+}
+
+// customLogger is a utlility to log messages to a number of destinations
+type customLogger struct {
+ logger *Logger
+ name string
+}
+
+// New creates a new customLogger. You may pass in a number of `io.Writer`s that
+// are the targets for the logs
+func newcustomLogger(logger *Logger, name string) *customLogger {
+ result := &customLogger{
+ name: name,
+ logger: logger,
+ }
+ return result
+}
+
+// Writeln writes directly to the output with no log level
+// Appends a carriage return to the message
+func (l *customLogger) Writeln(message string) error {
+ return l.logger.Writeln(message)
+}
+
+// Write writes directly to the output with no log level
+func (l *customLogger) Write(message string) error {
+ return l.logger.Write(message)
+}
+
+// Trace level logging. Works like Sprintf.
+func (l *customLogger) Trace(format string, args ...interface{}) error {
+ format = fmt.Sprintf("%s | %s", l.name, format)
+ return l.logger.processLogMessage(TRACE, format, args...)
+}
+
+// Debug level logging. Works like Sprintf.
+func (l *customLogger) Debug(format string, args ...interface{}) error {
+ format = fmt.Sprintf("%s | %s", l.name, format)
+ return l.logger.processLogMessage(DEBUG, format, args...)
+}
+
+// Info level logging. Works like Sprintf.
+func (l *customLogger) Info(format string, args ...interface{}) error {
+ format = fmt.Sprintf("%s | %s", l.name, format)
+ return l.logger.processLogMessage(INFO, format, args...)
+}
+
+// Warning level logging. Works like Sprintf.
+func (l *customLogger) Warning(format string, args ...interface{}) error {
+ format = fmt.Sprintf("%s | %s", l.name, format)
+ return l.logger.processLogMessage(WARNING, format, args...)
+}
+
+// Error level logging. Works like Sprintf.
+func (l *customLogger) Error(format string, args ...interface{}) error {
+ format = fmt.Sprintf("%s | %s", l.name, format)
+ return l.logger.processLogMessage(ERROR, format, args...)
+
+}
+
+// Fatal level logging. Works like Sprintf.
+func (l *customLogger) Fatal(format string, args ...interface{}) {
+ format = fmt.Sprintf("%s | %s", l.name, format)
+ l.logger.processLogMessage(FATAL, format, args...)
+ os.Exit(1)
+}
diff --git a/v2/internal/logger/default_logger.go b/v2/internal/logger/default_logger.go
new file mode 100644
index 000000000..82ed8c0ff
--- /dev/null
+++ b/v2/internal/logger/default_logger.go
@@ -0,0 +1,143 @@
+package logger
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "sync"
+)
+
+// Logger is a utlility to log messages to a number of destinations
+type Logger struct {
+ writers []io.Writer
+ logLevel uint8
+ showLevelInLog bool
+ lock sync.RWMutex
+}
+
+// New creates a new Logger. You may pass in a number of `io.Writer`s that
+// are the targets for the logs
+func New(writers ...io.Writer) *Logger {
+ result := &Logger{
+ logLevel: INFO,
+ showLevelInLog: true,
+ }
+ for _, writer := range writers {
+ result.AddOutput(writer)
+ }
+ return result
+}
+
+// Writers gets the log writers
+func (l *Logger) Writers() []io.Writer {
+ return l.writers
+}
+
+// CustomLogger creates a new custom logger that prints out a name/id
+// before the messages
+func (l *Logger) CustomLogger(name string) CustomLogger {
+ return newcustomLogger(l, name)
+}
+
+// HideLogLevel removes the loglevel text from the start of each logged line
+func (l *Logger) HideLogLevel() {
+ l.showLevelInLog = true
+}
+
+// SetLogLevel sets the minimum level of logs that will be output
+func (l *Logger) SetLogLevel(level uint8) {
+ l.logLevel = level
+}
+
+// AddOutput adds the given `io.Writer` to the list of destinations
+// that get logged to
+func (l *Logger) AddOutput(writer io.Writer) {
+ l.writers = append(l.writers, writer)
+}
+
+func (l *Logger) write(loglevel uint8, message string) error {
+
+ // Don't print logs lower than the current log level
+ if loglevel < l.logLevel {
+ return nil
+ }
+
+ // Show log level text if enabled
+ if l.showLevelInLog {
+ message = mapLogLevel[loglevel] + message
+ }
+
+ // write out the logs
+ l.lock.Lock()
+ for _, writer := range l.writers {
+ _, err := writer.Write([]byte(message))
+ if err != nil {
+ l.lock.Unlock() // Because defer is slow
+ return err
+ }
+ }
+ l.lock.Unlock()
+ return nil
+}
+
+// writeln appends a newline character to the message before writing
+func (l *Logger) writeln(loglevel uint8, message string) error {
+ return l.write(loglevel, message+"\n")
+}
+
+// Writeln writes directly to the output with no log level
+// Appends a carriage return to the message
+func (l *Logger) Writeln(message string) error {
+ return l.write(BYPASS, message+"\n")
+}
+
+// Write writes directly to the output with no log level
+func (l *Logger) Write(message string) error {
+ return l.write(BYPASS, message)
+}
+
+// processLogMessage formats the given message before writing it out
+func (l *Logger) processLogMessage(loglevel uint8, format string, args ...interface{}) error {
+ message := fmt.Sprintf(format, args...)
+ return l.writeln(loglevel, message)
+}
+
+// Trace level logging. Works like Sprintf.
+func (l *Logger) Trace(format string, args ...interface{}) error {
+ return l.processLogMessage(TRACE, format, args...)
+}
+
+// CustomTrace returns a custom Logging function that will insert the given name before the message
+func (l *Logger) CustomTrace(name string) func(format string, args ...interface{}) {
+ return func(format string, args ...interface{}) {
+ format = name + " | " + format
+ l.processLogMessage(TRACE, format, args...)
+ }
+}
+
+// Debug level logging. Works like Sprintf.
+func (l *Logger) Debug(format string, args ...interface{}) error {
+ return l.processLogMessage(DEBUG, format, args...)
+}
+
+// Info level logging. Works like Sprintf.
+func (l *Logger) Info(format string, args ...interface{}) error {
+ return l.processLogMessage(INFO, format, args...)
+}
+
+// Warning level logging. Works like Sprintf.
+func (l *Logger) Warning(format string, args ...interface{}) error {
+ return l.processLogMessage(WARNING, format, args...)
+}
+
+// Error level logging. Works like Sprintf.
+func (l *Logger) Error(format string, args ...interface{}) error {
+ return l.processLogMessage(ERROR, format, args...)
+
+}
+
+// Fatal level logging. Works like Sprintf.
+func (l *Logger) Fatal(format string, args ...interface{}) {
+ l.processLogMessage(FATAL, format, args...)
+ os.Exit(1)
+}
diff --git a/v2/internal/logger/logger.go b/v2/internal/logger/logger.go
new file mode 100644
index 000000000..3a00de13a
--- /dev/null
+++ b/v2/internal/logger/logger.go
@@ -0,0 +1,34 @@
+package logger
+
+const (
+ // TRACE level
+ TRACE uint8 = 0
+
+ // DEBUG level logging
+ DEBUG uint8 = 1
+
+ // INFO level logging
+ INFO uint8 = 2
+
+ // WARNING level logging
+ WARNING uint8 = 4
+
+ // ERROR level logging
+ ERROR uint8 = 8
+
+ // FATAL level logging
+ FATAL uint8 = 16
+
+ // BYPASS level logging - does not use a log level
+ BYPASS uint8 = 255
+)
+
+var mapLogLevel = map[uint8]string{
+ TRACE: "TRACE | ",
+ DEBUG: "DEBUG | ",
+ INFO: "INFO | ",
+ WARNING: "WARN | ",
+ ERROR: "ERROR | ",
+ FATAL: "FATAL | ",
+ BYPASS: "",
+}
diff --git a/v2/internal/logger/logger_test.go b/v2/internal/logger/logger_test.go
new file mode 100644
index 000000000..c54f2065d
--- /dev/null
+++ b/v2/internal/logger/logger_test.go
@@ -0,0 +1,202 @@
+package logger
+
+import (
+ "bytes"
+ "io/ioutil"
+ "log"
+ "os"
+ "testing"
+
+ "github.com/matryer/is"
+)
+
+func TestByteBufferLogger(t *testing.T) {
+
+ is := is.New(t)
+
+ // Create new byte buffer logger
+ var buf bytes.Buffer
+
+ myLogger := New(&buf)
+ myLogger.SetLogLevel(TRACE)
+
+ tests := map[uint8]string{
+ TRACE: "TRACE | I am a message!\n",
+ DEBUG: "DEBUG | I am a message!\n",
+ WARNING: "WARN | I am a message!\n",
+ INFO: "INFO | I am a message!\n",
+ ERROR: "ERROR | I am a message!\n",
+ }
+
+ methods := map[uint8]func(string, ...interface{}) error{
+ TRACE: myLogger.Trace,
+ DEBUG: myLogger.Debug,
+ WARNING: myLogger.Warning,
+ INFO: myLogger.Info,
+ ERROR: myLogger.Error,
+ }
+
+ for level, expected := range tests {
+
+ buf.Reset()
+
+ method := methods[level]
+
+ // Write message
+ err := method("I am a message!")
+ if err != nil {
+ panic(err)
+ }
+ actual := buf.String()
+
+ is.Equal(actual, expected)
+ }
+
+}
+func TestCustomLogger(t *testing.T) {
+
+ is := is.New(t)
+
+ // Create new byte buffer logger
+ var buf bytes.Buffer
+
+ myLogger := New(&buf)
+ myLogger.SetLogLevel(TRACE)
+ customLogger := myLogger.CustomLogger("Test")
+
+ tests := map[uint8]string{
+ TRACE: "TRACE | Test | I am a message!\n",
+ DEBUG: "DEBUG | Test | I am a message!\n",
+ WARNING: "WARN | Test | I am a message!\n",
+ INFO: "INFO | Test | I am a message!\n",
+ ERROR: "ERROR | Test | I am a message!\n",
+ }
+
+ methods := map[uint8]func(string, ...interface{}) error{
+ TRACE: customLogger.Trace,
+ DEBUG: customLogger.Debug,
+ WARNING: customLogger.Warning,
+ INFO: customLogger.Info,
+ ERROR: customLogger.Error,
+ }
+
+ for level, expected := range tests {
+
+ buf.Reset()
+
+ method := methods[level]
+
+ // Write message
+ err := method("I am a message!")
+ if err != nil {
+ panic(err)
+ }
+ actual := buf.String()
+
+ is.Equal(actual, expected)
+ }
+
+}
+func TestWriteln(t *testing.T) {
+
+ is := is.New(t)
+
+ // Create new byte buffer logger
+ var buf bytes.Buffer
+
+ myLogger := New(&buf)
+ myLogger.SetLogLevel(DEBUG)
+
+ buf.Reset()
+
+ // Write message
+ err := myLogger.Writeln("I am a message!")
+ if err != nil {
+ panic(err)
+ }
+ actual := buf.String()
+
+ is.Equal(actual, "I am a message!\n")
+
+ buf.Reset()
+
+ // Write message
+ err = myLogger.Write("I am a message!")
+ if err != nil {
+ panic(err)
+ }
+ actual = buf.String()
+
+ is.Equal(actual, "I am a message!")
+
+}
+
+func TestLogLevel(t *testing.T) {
+
+ is := is.New(t)
+
+ // Create new byte buffer logger
+ var buf bytes.Buffer
+
+ myLogger := New(&buf)
+ myLogger.SetLogLevel(ERROR)
+
+ tests := map[uint8]string{
+ TRACE: "",
+ DEBUG: "",
+ WARNING: "",
+ INFO: "",
+ ERROR: "ERROR | I am a message!\n",
+ }
+
+ methods := map[uint8]func(string, ...interface{}) error{
+ TRACE: myLogger.Trace,
+ DEBUG: myLogger.Debug,
+ WARNING: myLogger.Warning,
+ INFO: myLogger.Info,
+ ERROR: myLogger.Error,
+ }
+
+ for level := range tests {
+
+ method := methods[level]
+
+ // Write message
+ err := method("I am a message!")
+ if err != nil {
+ panic(err)
+ }
+
+ }
+ actual := buf.String()
+
+ is.Equal(actual, "ERROR | I am a message!\n")
+}
+
+func TestFileLogger(t *testing.T) {
+
+ is := is.New(t)
+
+ // Create new byte buffer logger
+ file, err := ioutil.TempFile(".", "wailsv2test")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.Remove(file.Name())
+
+ myLogger := New(file)
+ myLogger.SetLogLevel(DEBUG)
+
+ // Write message
+ err = myLogger.Info("I am a message!")
+ if err != nil {
+ panic(err)
+ }
+ actual, err := ioutil.ReadFile(file.Name())
+ if err != nil {
+ panic(err)
+ }
+
+ is.Equal(string(actual), "INFO | I am a message!\n")
+
+}
diff --git a/v2/internal/messagedispatcher/dispatchclient.go b/v2/internal/messagedispatcher/dispatchclient.go
new file mode 100644
index 000000000..58097e7a1
--- /dev/null
+++ b/v2/internal/messagedispatcher/dispatchclient.go
@@ -0,0 +1,86 @@
+package messagedispatcher
+
+import (
+ "fmt"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Client defines what a frontend client can do
+type Client interface {
+ Quit()
+ NotifyEvent(message string)
+ CallResult(message string)
+ SaveFileDialog(title string, filter string) string
+ OpenFileDialog(title string, filter string) string
+ OpenDirectoryDialog(title string, filter string) string
+ WindowSetTitle(title string)
+ WindowShow()
+ WindowHide()
+ WindowCenter()
+ WindowMaximise()
+ WindowUnmaximise()
+ WindowMinimise()
+ WindowUnminimise()
+ WindowPosition(x int, y int)
+ WindowSize(width int, height int)
+ WindowFullscreen()
+ WindowUnFullscreen()
+ WindowSetColour(colour string) bool
+}
+
+// DispatchClient is what the frontends use to interface with the
+// dispatcher
+type DispatchClient struct {
+ id string
+ logger logger.CustomLogger
+
+ bus *servicebus.ServiceBus
+
+ // Client
+ frontend Client
+}
+
+func newDispatchClient(id string, frontend Client, logger logger.CustomLogger, bus *servicebus.ServiceBus) *DispatchClient {
+
+ return &DispatchClient{
+ id: id,
+ frontend: frontend,
+ logger: logger,
+ bus: bus,
+ }
+
+}
+
+// DispatchMessage is called by the front ends. It is passed
+// an IPC message, translates it to a more concrete message
+// type then publishes it on the service bus.
+func (d *DispatchClient) DispatchMessage(incomingMessage string) {
+
+ // Parse the message
+ d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
+ parsedMessage, err := message.Parse(incomingMessage)
+ if err != nil {
+ d.logger.Trace("Error: " + err.Error())
+ return
+ }
+
+ // Save this client id
+ parsedMessage.ClientID = d.id
+
+ d.logger.Trace("I got a parsedMessage: %+v", parsedMessage)
+
+ // Check error
+ if err != nil {
+ d.logger.Trace("Error: " + err.Error())
+ // Hrm... what do we do with this?
+ d.bus.PublishForTarget("generic:message", incomingMessage, d.id)
+ return
+ }
+
+ // Publish the parsed message
+ d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id)
+
+}
diff --git a/v2/internal/messagedispatcher/message/call.go b/v2/internal/messagedispatcher/message/call.go
new file mode 100644
index 000000000..a3fb4b605
--- /dev/null
+++ b/v2/internal/messagedispatcher/message/call.go
@@ -0,0 +1,37 @@
+package message
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+type CallMessage struct {
+ Name string `json:"name"`
+ Args []interface{} `json:"args"`
+ CallbackID string `json:"callbackID,omitempty"`
+}
+
+// callMessageParser does what it says on the tin!
+func callMessageParser(message string) (*parsedMessage, error) {
+
+ // Sanity check: Call messages must be at least 3 bytes `C{}``
+ if len(message) < 3 {
+ return nil, fmt.Errorf("call message was an invalid length")
+ }
+
+ callMessage := new(CallMessage)
+
+ m := message[1:]
+ err := json.Unmarshal([]byte(m), callMessage)
+ if err != nil {
+ println(err.Error())
+ return nil, err
+ }
+
+ topic := "call:invoke"
+
+ // Create a new parsed message struct
+ parsedMessage := &parsedMessage{Topic: topic, Data: callMessage}
+
+ return parsedMessage, nil
+}
diff --git a/v2/internal/messagedispatcher/message/event.go b/v2/internal/messagedispatcher/message/event.go
new file mode 100644
index 000000000..0ce88dad5
--- /dev/null
+++ b/v2/internal/messagedispatcher/message/event.go
@@ -0,0 +1,47 @@
+package message
+
+import "fmt"
+
+import "encoding/json"
+
+type EventMessage struct {
+ Name string `json:"name"`
+ Data []interface{} `json:"data"`
+}
+
+type OnEventMessage struct {
+ Name string
+ Callback func(optionalData ...interface{})
+}
+
+// eventMessageParser does what it says on the tin!
+func eventMessageParser(message string) (*parsedMessage, error) {
+
+ // Sanity check: Event messages must be at least 2 bytes
+ if len(message) < 3 {
+ return nil, fmt.Errorf("event message was an invalid length")
+ }
+
+ eventMessage := new(EventMessage)
+ direction := message[1]
+
+ // Switch the event type (with or without data)
+ switch message[0] {
+ case 'e':
+ eventMessage.Name = message[2:]
+ case 'E':
+ m := message[2:]
+ err := json.Unmarshal([]byte(m), eventMessage)
+ if err != nil {
+ println(err.Error())
+ return nil, err
+ }
+ }
+
+ topic := "event:emit:from:" + string(direction)
+
+ // Create a new parsed message struct
+ parsedMessage := &parsedMessage{Topic: topic, Data: eventMessage}
+
+ return parsedMessage, nil
+}
diff --git a/v2/internal/messagedispatcher/message/log.go b/v2/internal/messagedispatcher/message/log.go
new file mode 100644
index 000000000..53c2c7c68
--- /dev/null
+++ b/v2/internal/messagedispatcher/message/log.go
@@ -0,0 +1,33 @@
+package message
+
+import "fmt"
+
+var logMessageMap = map[byte]string{
+ 'D': "log:debug",
+ 'I': "log:info",
+ 'W': "log:warning",
+ 'E': "log:error",
+ 'F': "log:fatal",
+}
+
+// logMessageParser does what it says on the tin!
+func logMessageParser(message string) (*parsedMessage, error) {
+
+ // Sanity check: Log messages must be at least 2 bytes
+ if len(message) < 2 {
+ return nil, fmt.Errorf("log message was an invalid length")
+ }
+
+ // Switch on the log type
+ messageTopic := logMessageMap[message[1]]
+
+ // If the type is invalid, raise error
+ if messageTopic == "" {
+ return nil, fmt.Errorf("log message type '%b' invalid", message[1])
+ }
+
+ // Create a new parsed message struct
+ parsedMessage := &parsedMessage{Topic: messageTopic, Data: message[2:]}
+
+ return parsedMessage, nil
+}
diff --git a/v2/internal/messagedispatcher/message/messageparser.go b/v2/internal/messagedispatcher/message/messageparser.go
new file mode 100644
index 000000000..58687e42c
--- /dev/null
+++ b/v2/internal/messagedispatcher/message/messageparser.go
@@ -0,0 +1,31 @@
+package message
+
+import "fmt"
+
+// Parse
+type parsedMessage struct {
+ Topic string
+ ClientID string
+ Data interface{}
+}
+
+// Map of different message parsers based on the header byte of the message
+var messageParsers = map[byte]func(string) (*parsedMessage, error){
+ 'L': logMessageParser,
+ 'R': runtimeMessageParser,
+ 'E': eventMessageParser,
+ 'e': eventMessageParser,
+ 'C': callMessageParser,
+ 'W': windowMessageParser,
+}
+
+// Parse will attempt to parse the given message
+func Parse(message string) (*parsedMessage, error) {
+
+ parseMethod := messageParsers[message[0]]
+ if parseMethod == nil {
+ return nil, fmt.Errorf("message type '%b' invalid", message[0])
+ }
+
+ return parseMethod(message)
+}
diff --git a/v2/internal/messagedispatcher/message/runtime.go b/v2/internal/messagedispatcher/message/runtime.go
new file mode 100644
index 000000000..6b8feea10
--- /dev/null
+++ b/v2/internal/messagedispatcher/message/runtime.go
@@ -0,0 +1,36 @@
+package message
+
+import "fmt"
+
+// runtimeMessageParser does what it says on the tin!
+func runtimeMessageParser(message string) (*parsedMessage, error) {
+
+ // Sanity check: Log messages must be at least 2 bytes
+ if len(message) < 3 {
+ return nil, fmt.Errorf("runtime message was an invalid length")
+ }
+
+ // Switch on the runtime module type
+ module := message[1]
+ switch module {
+ case 'B':
+ return processBrowserMessage(message)
+ }
+
+ return nil, fmt.Errorf("unknown message: %s", message)
+}
+
+// processBrowserMessage expects messages of the following format:
+// RB
+func processBrowserMessage(message string) (*parsedMessage, error) {
+ method := message[2]
+ switch method {
+ case 'U':
+ // Open URL
+ url := message[3:]
+ return &parsedMessage{Topic: "runtime:browser:openurl", Data: url}, nil
+ }
+
+ return nil, fmt.Errorf("unknown browser message: %s", message)
+
+}
diff --git a/v2/internal/messagedispatcher/message/window.go b/v2/internal/messagedispatcher/message/window.go
new file mode 100644
index 000000000..25e964a4d
--- /dev/null
+++ b/v2/internal/messagedispatcher/message/window.go
@@ -0,0 +1,76 @@
+package message
+
+import "fmt"
+
+// windowMessageParser does what it says on the tin!
+func windowMessageParser(message string) (*parsedMessage, error) {
+
+ // Sanity check: Window messages must be at least 2 bytes
+ if len(message) < 2 {
+ return nil, fmt.Errorf("window message was an invalid length")
+ }
+
+ // Extract event type
+ windowEvent := message[1]
+ parsedMessage := &parsedMessage{}
+
+ // Switch the windowEvent type
+ switch windowEvent {
+
+ // Closed window
+ case 'C':
+ parsedMessage.Topic = "quit"
+ parsedMessage.Data = "Window Closed"
+
+ // Center window
+ case 'c':
+ parsedMessage.Topic = "window:center"
+ parsedMessage.Data = ""
+
+ // Hide window
+ case 'H':
+ parsedMessage.Topic = "window:hide"
+ parsedMessage.Data = ""
+
+ // Show window
+ case 'S':
+ parsedMessage.Topic = "window:show"
+ parsedMessage.Data = ""
+
+ // Position window
+ case 'p':
+ parsedMessage.Topic = "window:position:" + message[3:]
+ parsedMessage.Data = ""
+
+ // Set window size
+ case 's':
+ parsedMessage.Topic = "window:size:" + message[3:]
+ parsedMessage.Data = ""
+
+ // Maximise window
+ case 'M':
+ parsedMessage.Topic = "window:maximise"
+ parsedMessage.Data = ""
+
+ // Unmaximise window
+ case 'U':
+ parsedMessage.Topic = "window:unmaximise"
+ parsedMessage.Data = ""
+
+ // Minimise window
+ case 'm':
+ parsedMessage.Topic = "window:minimise"
+ parsedMessage.Data = ""
+
+ // Unminimise window
+ case 'u':
+ parsedMessage.Topic = "window:unminimise"
+ parsedMessage.Data = ""
+
+ // Unknown event type
+ default:
+ return nil, fmt.Errorf("unknown message: %s", message)
+ }
+
+ return parsedMessage, nil
+}
diff --git a/v2/internal/messagedispatcher/messagedispatcher.go b/v2/internal/messagedispatcher/messagedispatcher.go
new file mode 100644
index 000000000..5787a4724
--- /dev/null
+++ b/v2/internal/messagedispatcher/messagedispatcher.go
@@ -0,0 +1,391 @@
+package messagedispatcher
+
+import (
+ "encoding/json"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/wailsapp/wails/v2/internal/crypto"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Dispatcher translates messages received from the frontend
+// and publishes them onto the service bus
+type Dispatcher struct {
+ quitChannel <-chan *servicebus.Message
+ resultChannel <-chan *servicebus.Message
+ eventChannel <-chan *servicebus.Message
+ windowChannel <-chan *servicebus.Message
+ dialogChannel <-chan *servicebus.Message
+ running bool
+
+ servicebus *servicebus.ServiceBus
+ logger logger.CustomLogger
+
+ // Clients
+ clients map[string]*DispatchClient
+ lock sync.RWMutex
+}
+
+// New dispatcher. Needs a service bus to send to.
+func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher, error) {
+ // Subscribe to call result messages
+ resultChannel, err := servicebus.Subscribe("call:result")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to event messages
+ eventChannel, err := servicebus.Subscribe("event:emit")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to quit messages
+ quitChannel, err := servicebus.Subscribe("quit")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to window messages
+ windowChannel, err := servicebus.Subscribe("window")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to dialog events
+ dialogChannel, err := servicebus.Subscribe("dialog:select")
+ if err != nil {
+ return nil, err
+ }
+
+ result := &Dispatcher{
+ servicebus: servicebus,
+ eventChannel: eventChannel,
+ logger: logger.CustomLogger("Message Dispatcher"),
+ clients: make(map[string]*DispatchClient),
+ resultChannel: resultChannel,
+ quitChannel: quitChannel,
+ windowChannel: windowChannel,
+ dialogChannel: dialogChannel,
+ }
+
+ return result, nil
+}
+
+// Start the subsystem
+func (d *Dispatcher) Start() error {
+
+ d.logger.Trace("Starting")
+
+ d.running = true
+
+ // Spin off a go routine
+ go func() {
+ for d.running {
+ select {
+ case <-d.quitChannel:
+ d.processQuit()
+ d.running = false
+ case resultMessage := <-d.resultChannel:
+ d.processCallResult(resultMessage)
+ case eventMessage := <-d.eventChannel:
+ d.processEvent(eventMessage)
+ case windowMessage := <-d.windowChannel:
+ d.processWindowMessage(windowMessage)
+ case dialogMessage := <-d.dialogChannel:
+ d.processDialogMessage(dialogMessage)
+ }
+ }
+
+ // Call shutdown
+ d.shutdown()
+ }()
+
+ return nil
+}
+
+func (d *Dispatcher) processQuit() {
+ d.lock.RLock()
+ defer d.lock.RUnlock()
+ for _, client := range d.clients {
+ client.frontend.Quit()
+ }
+}
+
+func (d *Dispatcher) shutdown() {
+ d.logger.Trace("Shutdown")
+}
+
+// RegisterClient will register the given callback with the dispatcher
+// and return a DispatchClient that the caller can use to send messages
+func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
+ d.lock.Lock()
+ defer d.lock.Unlock()
+
+ // Create ID
+ id := d.getUniqueID()
+ d.clients[id] = newDispatchClient(id, client, d.logger, d.servicebus)
+
+ return d.clients[id]
+}
+
+// RemoveClient will remove the registered client
+func (d *Dispatcher) RemoveClient(dc *DispatchClient) {
+ d.lock.Lock()
+ defer d.lock.Unlock()
+ delete(d.clients, dc.id)
+}
+
+func (d *Dispatcher) getUniqueID() string {
+ var uid string
+ for {
+ uid = crypto.RandomID()
+
+ if d.clients[uid] == nil {
+ break
+ }
+ }
+
+ return uid
+}
+
+func (d *Dispatcher) processCallResult(result *servicebus.Message) {
+ target := result.Target()
+
+ if target == "" {
+ // This is an error. Calls are 1:1!
+ d.logger.Fatal("No target for call result: %+v", result)
+ }
+
+ d.lock.RLock()
+ client := d.clients[target]
+ d.lock.RUnlock()
+ if client == nil {
+ // This is fatal - unknown target!
+ d.logger.Fatal("Unknown target for call result: %+v", result)
+ }
+
+ d.logger.Trace("Sending message to client %s: R%s", target, result.Data().(string))
+ client.frontend.CallResult(result.Data().(string))
+}
+
+// processEvent will
+func (d *Dispatcher) processEvent(result *servicebus.Message) {
+
+ d.logger.Trace("Got event in message dispatcher: %+v", result)
+
+ splitTopic := strings.Split(result.Topic(), ":")
+ eventType := splitTopic[1]
+ switch eventType {
+ case "emit":
+ eventFrom := splitTopic[3]
+ if eventFrom == "g" {
+ // This was sent from Go - notify frontend
+ eventData := result.Data().(*message.EventMessage)
+ // Unpack event
+ payload, err := json.Marshal(eventData)
+ if err != nil {
+ d.logger.Error("Unable to marshal eventData: %s", err.Error())
+ return
+ }
+ d.lock.RLock()
+ for _, client := range d.clients {
+ client.frontend.NotifyEvent(string(payload))
+ }
+ d.lock.RUnlock()
+ }
+ default:
+ d.logger.Error("Unknown event type: %s", eventType)
+ }
+}
+
+// processWindowMessage processes messages intended for the window
+func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
+ d.lock.RLock()
+ defer d.lock.RUnlock()
+ splitTopic := strings.Split(result.Topic(), ":")
+ command := splitTopic[1]
+ switch command {
+ case "settitle":
+ title, ok := result.Data().(string)
+ if !ok {
+ d.logger.Error("Invalid title for 'window:settitle' : %#v", result.Data())
+ return
+ }
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowSetTitle(title)
+ }
+ case "fullscreen":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowFullscreen()
+ }
+ case "unfullscreen":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowUnFullscreen()
+ }
+ case "setcolour":
+ colour, ok := result.Data().(string)
+ if !ok {
+ d.logger.Error("Invalid colour for 'window:setcolour' : %#v", result.Data())
+ return
+ }
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowSetColour(colour)
+ }
+ case "show":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowShow()
+ }
+ case "hide":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowHide()
+ }
+ case "center":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowCenter()
+ }
+ case "maximise":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowMaximise()
+ }
+ case "unmaximise":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowUnmaximise()
+ }
+ case "minimise":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowMinimise()
+ }
+ case "unminimise":
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowUnminimise()
+ }
+ case "position":
+ // We need 2 arguments
+ if len(splitTopic) != 4 {
+ d.logger.Error("Invalid number of parameters for 'window:position' : %#v", result.Data())
+ return
+ }
+ x, err1 := strconv.Atoi(splitTopic[2])
+ y, err2 := strconv.Atoi(splitTopic[3])
+ if err1 != nil || err2 != nil {
+ d.logger.Error("Invalid integer parameters for 'window:position' : %#v", result.Data())
+ return
+ }
+ // Notify clients
+ for _, client := range d.clients {
+ client.frontend.WindowPosition(x, y)
+ }
+ case "size":
+ // We need 2 arguments
+ if len(splitTopic) != 4 {
+ d.logger.Error("Invalid number of parameters for 'window:size' : %#v", result.Data())
+ return
+ }
+ w, err1 := strconv.Atoi(splitTopic[2])
+ h, err2 := strconv.Atoi(splitTopic[3])
+ if err1 != nil || err2 != nil {
+ d.logger.Error("Invalid integer parameters for 'window:size' : %#v", result.Data())
+ return
+ }
+ // Notifh clients
+ for _, client := range d.clients {
+ client.frontend.WindowSize(w, h)
+ }
+ default:
+ d.logger.Error("Unknown window command: %s", command)
+ }
+ d.logger.Trace("Got window in message dispatcher: %+v", result)
+
+}
+
+// processDialogMessage processes dialog messages
+func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
+ splitTopic := strings.Split(result.Topic(), ":")
+
+ if len(splitTopic) < 4 {
+ d.logger.Error("Invalid dialog message : %#v", result.Data())
+ return
+ }
+
+ command := splitTopic[1]
+ switch command {
+ case "select":
+ dialogType := splitTopic[2]
+ title := splitTopic[3]
+ filter := ""
+ if len(splitTopic) > 4 {
+ filter = splitTopic[4]
+ }
+ switch dialogType {
+ case "file":
+ responseTopic, ok := result.Data().(string)
+ if !ok {
+ d.logger.Error("Invalid responseTopic for 'dialog:select:file' : %#v", result.Data())
+ return
+ }
+ d.logger.Info("Opening File dialog! responseTopic = %s", responseTopic)
+
+ // TODO: Work out what we mean in a multi window environment...
+ // For now we will just pick the first one
+ var result string
+ for _, client := range d.clients {
+ result = client.frontend.OpenFileDialog(title, filter)
+ }
+
+ // Send dummy response
+ d.servicebus.Publish(responseTopic, result)
+ case "filesave":
+ responseTopic, ok := result.Data().(string)
+ if !ok {
+ d.logger.Error("Invalid responseTopic for 'dialog:select:filesave' : %#v", result.Data())
+ return
+ }
+ d.logger.Info("Opening Save File dialog! responseTopic = %s", responseTopic)
+
+ // TODO: Work out what we mean in a multi window environment...
+ // For now we will just pick the first one
+ var result string
+ for _, client := range d.clients {
+ result = client.frontend.SaveFileDialog(title, filter)
+ }
+
+ // Send dummy response
+ d.servicebus.Publish(responseTopic, result)
+ case "directory":
+ responseTopic, ok := result.Data().(string)
+ if !ok {
+ d.logger.Error("Invalid responseTopic for 'dialog:select:directory' : %#v", result.Data())
+ return
+ }
+ d.logger.Info("Opening Directory dialog! responseTopic = %s", responseTopic)
+
+ // TODO: Work out what we mean in a multi window environment...
+ // For now we will just pick the first one
+ var result string
+ for _, client := range d.clients {
+ result = client.frontend.OpenDirectoryDialog(title, filter)
+ }
+ // Send dummy response
+ d.servicebus.Publish(responseTopic, result)
+
+ default:
+ d.logger.Error("Unknown dialog command: %s", command)
+ }
+ }
+}
diff --git a/v2/internal/parse/README.md b/v2/internal/parse/README.md
new file mode 100644
index 000000000..5a40811a3
--- /dev/null
+++ b/v2/internal/parse/README.md
@@ -0,0 +1,10 @@
+# Parse
+
+Parse will attempt to parse your Wails project to perform a number of tasks:
+ * Verify that you have bound struct pointers
+ * Generate JS helper files/docs
+
+It currently checks bindings correctly if your code binds using one of the following methods:
+ * Literal Binding: `app.Bind(&MyStruct{})`
+ * Variable Binding: `app.Bind(m)` - m can be `m := &MyStruct{}` or `m := newMyStruct()`
+ * Function Binding: `app.Bind(newMyStruct())`
diff --git a/v2/internal/parse/parse.go b/v2/internal/parse/parse.go
new file mode 100644
index 000000000..f448c088d
--- /dev/null
+++ b/v2/internal/parse/parse.go
@@ -0,0 +1,441 @@
+package main
+
+import (
+ "fmt"
+ "go/ast"
+ "os"
+ "strings"
+
+ "github.com/leaanthony/slicer"
+ "golang.org/x/tools/go/packages"
+)
+
+var internalMethods = slicer.String([]string{"WailsInit", "Wails Shutdown"})
+
+var structCache = make(map[string]*ParsedStruct)
+var boundStructs = make(map[string]*ParsedStruct)
+var boundMethods = []string{}
+var boundStructPointerLiterals = []string{}
+var boundStructLiterals = slicer.StringSlicer{}
+var boundVariables = slicer.StringSlicer{}
+var app = ""
+var structPointerFunctionDecls = make(map[string]string)
+var structFunctionDecls = make(map[string]string)
+var variableStructDecls = make(map[string]string)
+var variableFunctionDecls = make(map[string]string)
+
+type Parameter struct {
+ Name string
+ Type string
+}
+
+type ParsedMethod struct {
+ Struct string
+ Name string
+ Comments []string
+ Inputs []*Parameter
+ Returns []*Parameter
+}
+
+type ParsedStruct struct {
+ Name string
+ Methods []*ParsedMethod
+}
+
+type BoundStructs []*ParsedStruct
+
+func ParseProject(projectPath string) (BoundStructs, error) {
+
+ cfg := &packages.Config{Mode: packages.NeedFiles | packages.NeedSyntax | packages.NeedTypesInfo}
+ pkgs, err := packages.Load(cfg, projectPath)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "load: %v\n", err)
+ os.Exit(1)
+ }
+ if packages.PrintErrors(pkgs) > 0 {
+ os.Exit(1)
+ }
+
+ // Iterate the packages
+ for _, pkg := range pkgs {
+
+ // Iterate the files
+ for _, file := range pkg.Syntax {
+
+ var wailsPkgVar = ""
+
+ ast.Inspect(file, func(n ast.Node) bool {
+ var s string
+ switch x := n.(type) {
+ // Parse import declarations
+ case *ast.ImportSpec:
+ // Determine what wails has been imported as
+ if x.Path.Value == `"github.com/wailsapp/wails/v2"` {
+ wailsPkgVar = x.Name.Name
+ }
+ // Parse calls. We are looking for app.Bind() calls
+ case *ast.CallExpr:
+ f, ok := x.Fun.(*ast.SelectorExpr)
+ if ok {
+ n, ok := f.X.(*ast.Ident)
+ if ok {
+ //Check this is the Bind() call associated with the app variable
+ if n.Name == app && f.Sel.Name == "Bind" {
+ if len(x.Args) == 1 {
+ ce, ok := x.Args[0].(*ast.CallExpr)
+ if ok {
+ n, ok := ce.Fun.(*ast.Ident)
+ if ok {
+ // We found a bind method using a function call
+ // EG: app.Bind( newMyStruct() )
+ boundMethods = append(boundMethods, n.Name)
+ }
+ } else {
+ // We also want to check for Bind( &MyStruct{} )
+ ue, ok := x.Args[0].(*ast.UnaryExpr)
+ if ok {
+ if ue.Op.String() == "&" {
+ cl, ok := ue.X.(*ast.CompositeLit)
+ if ok {
+ t, ok := cl.Type.(*ast.Ident)
+ if ok {
+ // We have found Bind( &MyStruct{} )
+ boundStructPointerLiterals = append(boundStructPointerLiterals, t.Name)
+ }
+ }
+ }
+ } else {
+ // Let's check when the user binds a struct,
+ // rather than a struct pointer: Bind( MyStruct{} )
+ // We do this to provide better hints to the user
+ cl, ok := x.Args[0].(*ast.CompositeLit)
+ if ok {
+ t, ok := cl.Type.(*ast.Ident)
+ if ok {
+ boundStructLiterals.Add(t.Name)
+ }
+ } else {
+ // Also check for when we bind a variable
+ // myVariable := &MyStruct{}
+ // app.Bind( myVariable )
+ i, ok := x.Args[0].(*ast.Ident)
+ if ok {
+ boundVariables.Add(i.Name)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // We scan assignments for a number of reasons:
+ // * Determine the variable containing the main application
+ // * Determine the type of variables that get used in Bind()
+ // * Determine the type of variables that get created with var := &MyStruct{}
+ case *ast.AssignStmt:
+ for _, rhs := range x.Rhs {
+ ce, ok := rhs.(*ast.CallExpr)
+ if ok {
+ se, ok := ce.Fun.(*ast.SelectorExpr)
+ if ok {
+ i, ok := se.X.(*ast.Ident)
+ if ok {
+ // Have we found the wails package name?
+ if i.Name == wailsPkgVar {
+ // Check we are calling a function to create the app
+ if se.Sel.Name == "CreateApp" || se.Sel.Name == "CreateAppWithOptions" {
+ if len(x.Lhs) == 1 {
+ i, ok := x.Lhs[0].(*ast.Ident)
+ if ok {
+ // Found the app variable name
+ app = i.Name
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // Check for function assignment
+ // a := newMyStruct()
+ fe, ok := ce.Fun.(*ast.Ident)
+ if ok {
+ if len(x.Lhs) == 1 {
+ i, ok := x.Lhs[0].(*ast.Ident)
+ if ok {
+ // Store the variable -> Function mapping
+ // so we can later resolve the type
+ variableFunctionDecls[i.Name] = fe.Name
+ }
+ }
+ }
+ }
+ } else {
+ // Check for literal assignment of struct
+ // EG: myvar := MyStruct{}
+ ue, ok := rhs.(*ast.UnaryExpr)
+ if ok {
+ cl, ok := ue.X.(*ast.CompositeLit)
+ if ok {
+ t, ok := cl.Type.(*ast.Ident)
+ if ok {
+ if len(x.Lhs) == 1 {
+ i, ok := x.Lhs[0].(*ast.Ident)
+ if ok {
+ variableStructDecls[i.Name] = t.Name
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // We scan for functions to build up a list of function names
+ // for a number of reasons:
+ // * Determine which functions are struct methods that are bound
+ // * Determine
+ case *ast.FuncDecl:
+ if x.Recv != nil {
+ // This is a struct method
+ for _, field := range x.Recv.List {
+ se, ok := field.Type.(*ast.StarExpr)
+ if ok {
+ // This is a struct pointer method
+ i, ok := se.X.(*ast.Ident)
+ if ok {
+ // We want to ignore Internal functions
+ if internalMethods.Contains(x.Name.Name) {
+ continue
+ }
+ // If we haven't already found this struct,
+ // Create a placeholder in the cache
+ parsedStruct := structCache[i.Name]
+ if parsedStruct == nil {
+ structCache[i.Name] = &ParsedStruct{
+ Name: i.Name,
+ }
+ parsedStruct = structCache[i.Name]
+ }
+
+ // If this method is Public
+ if string(x.Name.Name[0]) == strings.ToUpper((string(x.Name.Name[0]))) {
+ structMethod := &ParsedMethod{
+ Struct: i.Name,
+ Name: x.Name.Name,
+ }
+ // Check if the method has comments.
+ // If so, save it with the parsed method
+ if x.Doc != nil {
+ for _, comment := range x.Doc.List {
+ stringComment := comment.Text
+ if strings.HasPrefix(stringComment, "//") {
+ stringComment = stringComment[2:]
+ }
+ structMethod.Comments = append(structMethod.Comments, strings.TrimSpace(stringComment))
+ }
+ }
+
+ // Save the input parameters
+ for _, inputField := range x.Type.Params.List {
+ t, ok := inputField.Type.(*ast.Ident)
+ if !ok {
+ continue
+ }
+ for _, name := range inputField.Names {
+ structMethod.Inputs = append(structMethod.Inputs, &Parameter{Name: name.Name, Type: t.Name})
+ }
+ }
+
+ // Save the output parameters
+ for _, outputField := range x.Type.Results.List {
+ t, ok := outputField.Type.(*ast.Ident)
+ if !ok {
+ continue
+ }
+ if len(outputField.Names) == 0 {
+ structMethod.Returns = append(structMethod.Returns, &Parameter{Type: t.Name})
+ } else {
+ for _, name := range outputField.Names {
+ structMethod.Returns = append(structMethod.Returns, &Parameter{Name: name.Name, Type: t.Name})
+ }
+ }
+ }
+
+ // Append this method to the parsed struct
+ parsedStruct.Methods = append(parsedStruct.Methods, structMethod)
+
+ }
+ }
+ }
+ }
+ } else {
+ // This is a function declaration
+ // We care about its name and return type
+ // This will allow us to resolve types later
+ functionName := x.Name.Name
+
+ // Look for one that returns a single value
+ if x.Type != nil && x.Type.Results != nil && x.Type.Results.List != nil {
+ if len(x.Type.Results.List) == 1 {
+ // Check for *struct
+ t, ok := x.Type.Results.List[0].Type.(*ast.StarExpr)
+ if ok {
+ s, ok := t.X.(*ast.Ident)
+ if ok {
+ // println("*** Function", functionName, "found which returns: *"+s.Name)
+ structPointerFunctionDecls[functionName] = s.Name
+ }
+ } else {
+ // Check for functions that return a struct
+ // This is to help us provide hints if the user binds a struct
+ t, ok := x.Type.Results.List[0].Type.(*ast.Ident)
+ if ok {
+ // println("*** Function", functionName, "found which returns: "+t.Name)
+ structFunctionDecls[functionName] = t.Name
+ }
+ }
+ }
+ }
+ }
+ }
+ return true
+ })
+ // spew.Dump(file)
+ }
+ }
+
+ /***** Update bound structs ******/
+
+ // Resolve bound Methods
+ for _, method := range boundMethods {
+ s, ok := structPointerFunctionDecls[method]
+ if !ok {
+ s, ok = structFunctionDecls[method]
+ if !ok {
+ println("Fatal: Bind statement using", method, "but cannot find", method, "declaration")
+ } else {
+ println("Fatal: Cannot bind struct using method `" + method + "` because it returns a struct (" + s + "). Return a pointer to " + s + " instead.")
+ }
+ os.Exit(1)
+ }
+ structDefinition := structCache[s]
+ if structDefinition == nil {
+ println("Fatal: Bind statement using `"+method+"` but cannot find struct", s, "definition")
+ os.Exit(1)
+ }
+ boundStructs[s] = structDefinition
+ }
+
+ // Resolve bound vars
+ for _, structLiteral := range boundStructPointerLiterals {
+ s, ok := structCache[structLiteral]
+ if !ok {
+ println("Fatal: Bind statement using", structLiteral, "but cannot find", structLiteral, "declaration")
+ os.Exit(1)
+ }
+ boundStructs[structLiteral] = s
+ }
+
+ // Resolve bound variables
+ boundVariables.Each(func(variable string) {
+ v, ok := variableStructDecls[variable]
+ if !ok {
+ method, ok := variableFunctionDecls[variable]
+ if !ok {
+ println("Fatal: Bind statement using variable `" + variable + "` which does not resolve to a struct pointer")
+ os.Exit(1)
+ }
+
+ // Resolve function name
+ v, ok = structPointerFunctionDecls[method]
+ if !ok {
+ v, ok = structFunctionDecls[method]
+ if !ok {
+ println("Fatal: Bind statement using", method, "but cannot find", method, "declaration")
+ } else {
+ println("Fatal: Cannot bind variable `" + variable + "` because it resolves to a struct (" + v + "). Return a pointer to " + v + " instead.")
+ }
+ os.Exit(1)
+ }
+
+ }
+
+ s, ok := structCache[v]
+ if !ok {
+ println("Fatal: Bind statement using variable `" + variable + "` which resolves to a `" + v + "` but cannot find its declaration")
+ os.Exit(1)
+ }
+ boundStructs[v] = s
+
+ })
+
+ // Check for struct literals
+ boundStructLiterals.Each(func(structName string) {
+ println("Fatal: Cannot bind struct using struct literal `" + structName + "{}`. Create a pointer to " + structName + " instead.")
+ os.Exit(1)
+ })
+
+ // Check for bound variables
+ // boundVariables.Each(func(varName string) {
+ // println("Fatal: Cannot bind struct using struct literal `" + structName + "{}`. Create a pointer to " + structName + " instead.")
+ // })
+
+ // spew.Dump(boundStructs)
+ // os.Exit(0)
+
+ // }
+ // Inspect the AST and print all identifiers and literals.
+
+ println("export {")
+
+ noOfStructs := len(boundStructs)
+ structCount := 0
+ for _, s := range boundStructs {
+ structCount++
+ println()
+ println(" " + s.Name + ": {")
+ println()
+ noOfMethods := len(s.Methods)
+ for methodCount, m := range s.Methods {
+ println(" /****************")
+ for _, comment := range m.Comments {
+ println(" *", comment)
+ }
+ if len(m.Comments) > 0 {
+ println(" *")
+ }
+ inputNames := ""
+ for _, input := range m.Inputs {
+ println(" * @param {"+input.Type+"}", input.Name)
+ inputNames += input.Name + ", "
+ }
+ print(" * @return Promise<")
+ for _, output := range m.Returns {
+ print(output.Type + "|")
+ }
+ println("Error>")
+ println(" *")
+ println(" ***/")
+ if len(inputNames) > 2 {
+ inputNames = inputNames[:len(inputNames)-2]
+ }
+ println(" ", m.Name+": function("+inputNames+") {")
+ println(" return window.backend." + s.Name + "." + m.Name + "(" + inputNames + ");")
+ print(" }")
+ if methodCount < noOfMethods-1 {
+ print(",")
+ }
+ println()
+ println()
+ }
+ print(" }")
+ if structCount < noOfStructs-1 {
+ print(",")
+ }
+ println()
+ }
+ println()
+ println("}")
+ println()
+}
diff --git a/v2/internal/process/process.go b/v2/internal/process/process.go
new file mode 100644
index 000000000..4a92e8fd9
--- /dev/null
+++ b/v2/internal/process/process.go
@@ -0,0 +1,63 @@
+package process
+
+import (
+ "os/exec"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+)
+
+// Process defines a process that can be executed
+type Process struct {
+ logger *logger.Logger
+ cmd *exec.Cmd
+ exitChannel chan bool
+ Running bool
+}
+
+// NewProcess creates a new process struct
+func NewProcess(logger *logger.Logger, cmd string, args ...string) *Process {
+ return &Process{
+ logger: logger,
+ cmd: exec.Command(cmd, args...),
+ exitChannel: make(chan bool, 1),
+ }
+}
+
+// Start the process
+func (p *Process) Start() error {
+
+ err := p.cmd.Start()
+ if err != nil {
+ return err
+ }
+
+ p.Running = true
+
+ go func(cmd *exec.Cmd, running *bool, logger *logger.Logger, exitChannel chan bool) {
+ logger.Info("Starting process (PID: %d)", cmd.Process.Pid)
+ cmd.Wait()
+ logger.Info("Exiting process (PID: %d)", cmd.Process.Pid)
+ *running = false
+ exitChannel <- true
+ }(p.cmd, &p.Running, p.logger, p.exitChannel)
+
+ return nil
+}
+
+// Kill the process
+func (p *Process) Kill() error {
+ if !p.Running {
+ return nil
+ }
+ err := p.cmd.Process.Kill()
+
+ // Wait for command to exit properly
+ <-p.exitChannel
+
+ return err
+}
+
+// PID returns the process PID
+func (p *Process) PID() int {
+ return p.cmd.Process.Pid
+}
diff --git a/v2/internal/project/project.go b/v2/internal/project/project.go
new file mode 100644
index 000000000..10e707b82
--- /dev/null
+++ b/v2/internal/project/project.go
@@ -0,0 +1,80 @@
+package project
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "path/filepath"
+ "runtime"
+ "strings"
+)
+
+// Project holds the data related to a Wails project
+type Project struct {
+
+ /*** Application Data ***/
+ Name string `json:"name"`
+
+ // Application HTML, JS and CSS filenames
+ HTML string `json:"html"`
+ JS string `json:"js"`
+ CSS string `json:"css"`
+ BuildCommand string `json:"frontend:build"`
+ InstallCommand string `json:"frontend:install"`
+ /*** Internal Data ***/
+
+ // The path to the project directory
+ Path string
+
+ // The output filename
+ OutputFilename string `json:"outputfilename"`
+
+ // The type of application. EG: Desktop, Server, etc
+ OutputType string
+
+ // The platform to target
+ Platform string
+}
+
+// Load the project from the current working directory
+func Load(projectPath string) (*Project, error) {
+
+ // Attempt to load project.json
+ projectFile := filepath.Join(projectPath, "wails.json")
+ rawBytes, err := ioutil.ReadFile(projectFile)
+ if err != nil {
+ return nil, err
+ }
+
+ // Unmarshal JSON
+ var result Project
+ err = json.Unmarshal(rawBytes, &result)
+ if err != nil {
+ return nil, err
+ }
+
+ // Fix up our project paths
+ result.Path = filepath.ToSlash(projectPath) + "/"
+ result.HTML = filepath.Join(projectPath, result.HTML)
+ result.JS = filepath.Join(projectPath, result.JS)
+ result.CSS = filepath.Join(projectPath, result.CSS)
+
+ // Create default name if not given
+ if result.Name == "" {
+ result.Name = "wailsapp"
+ }
+
+ // Fix up OutputFilename
+ switch runtime.GOOS {
+ case "windows":
+ if !strings.HasSuffix(result.OutputFilename, ".exe") {
+ result.OutputFilename += ".exe"
+ }
+ case "darwin", "linux":
+ if strings.HasSuffix(result.OutputFilename, ".exe") {
+ result.OutputFilename = strings.TrimSuffix(result.OutputFilename, ".exe")
+ }
+ }
+
+ // Return our project data
+ return &result, nil
+}
diff --git a/v2/internal/runtime/assets/wails.js b/v2/internal/runtime/assets/wails.js
new file mode 100644
index 000000000..e67192d53
--- /dev/null
+++ b/v2/internal/runtime/assets/wails.js
@@ -0,0 +1 @@
+!function(n){var e={};function t(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,r){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:r})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(r,o,function(e){return n[e]}.bind(null,o));return r},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){"use strict";t.r(e);var r={};t.r(r),t.d(r,"Debug",(function(){return u})),t.d(r,"Info",(function(){return l})),t.d(r,"Warning",(function(){return d})),t.d(r,"Error",(function(){return f})),t.d(r,"Fatal",(function(){return s}));var o={};t.r(o),t.d(o,"OpenURL",(function(){return w})),t.d(o,"OpenFile",(function(){return p}));var i=[];function a(n){!function(n){if(window.wailsbridge?window.wailsbridge.websocket.send(n):(window.external.invoke(n),console.log("Invoke called with: "+n)),i.length>0)for(var e=0;e1){var e={name:n,data:[].slice.apply(arguments).slice(1)};a("Ej"+JSON.stringify(e))}else a("ej"+n)}var b={};var m,E={};function O(n,e,t){return null!=t&&null!=t||(t=0),new Promise((function(r,o){var i;do{i=n+"-"+m()}while(E[i]);if(t>0)var c=setTimeout((function(){o(Error("Call to "+n+" timed out. Request ID: "+i))}),t);E[i]={timeoutHandle:c,reject:o,resolve:r};try{JSON.stringify(e);a("call")}catch(n){console.error(n)}}))}function j(n){try{return new Function("var "+n),!0}catch(n){return!1}}function S(){return(S=Object.assign||function(n){for(var e=1;e1)for(var r=0;r 0 {
+ title = params[0]
+ }
+
+ if len(params) > 1 {
+ filter = params[1]
+ }
+
+ return title, filter
+}
+
+// SelectFile prompts the user to select a file
+func (r *dialog) SelectFile(params ...string) string {
+
+ // Extract title + filter
+ title, filter := r.processTitleAndFilter(params...)
+
+ // Create unique dialog callback
+ uniqueCallback := crypto.RandomID()
+
+ // Subscribe to the respose channel
+ responseTopic := "dialog:fileselected:" + uniqueCallback
+ dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
+ if err != nil {
+ fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
+ }
+
+ // Publish dialog request
+ message := "dialog:select:file:" + title
+ if filter != "" {
+ message += ":" + filter
+ }
+ r.bus.Publish(message, responseTopic)
+
+ // Wait for result
+ result := <-dialogResponseChannel
+
+ // Delete subscription to response topic
+ r.bus.UnSubscribe(responseTopic)
+
+ return result.Data().(string)
+}
+
+// SaveFile prompts the user to select a file to save to
+func (r *dialog) SaveFile(params ...string) string {
+
+ // Extract title + filter
+ title, filter := r.processTitleAndFilter(params...)
+
+ // Create unique dialog callback
+ uniqueCallback := crypto.RandomID()
+
+ // Subscribe to the respose channel
+ responseTopic := "dialog:filesaveselected:" + uniqueCallback
+ dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
+ if err != nil {
+ fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
+ }
+
+ // Publish dialog request
+ message := "dialog:select:filesave:" + title
+ if filter != "" {
+ message += ":" + filter
+ }
+ r.bus.Publish(message, responseTopic)
+
+ // Wait for result
+ result := <-dialogResponseChannel
+
+ // Delete subscription to response topic
+ r.bus.UnSubscribe(responseTopic)
+
+ return result.Data().(string)
+}
+
+// SelectDirectory prompts the user to select a file
+func (r *dialog) SelectDirectory(params ...string) string {
+
+ // Extract title + filter
+ title, filter := r.processTitleAndFilter(params...)
+
+ // Create unique dialog callback
+ uniqueCallback := crypto.RandomID()
+
+ // Subscribe to the respose channel
+ responseTopic := "dialog:directoryselected:" + uniqueCallback
+ dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
+ if err != nil {
+ fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
+ }
+
+ // Publish dialog request
+ message := "dialog:select:directory:" + title
+ if filter != "" {
+ message += ":" + filter
+ }
+ r.bus.Publish(message, responseTopic)
+
+ // Wait for result
+ var result *servicebus.Message = <-dialogResponseChannel
+
+ // Delete subscription to response topic
+ r.bus.UnSubscribe(responseTopic)
+
+ return result.Data().(string)
+}
diff --git a/v2/internal/runtime/goruntime/events.go b/v2/internal/runtime/goruntime/events.go
new file mode 100644
index 000000000..bba45bd73
--- /dev/null
+++ b/v2/internal/runtime/goruntime/events.go
@@ -0,0 +1,43 @@
+package goruntime
+
+import (
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Events defines all events related operations
+type Events interface {
+ On(eventName string, callback func(optionalData ...interface{}))
+ Emit(eventName string, optionalData ...interface{})
+}
+
+// event exposes the events interface
+type event struct {
+ bus *servicebus.ServiceBus
+}
+
+// newEvents creates a new Events struct
+func newEvents(bus *servicebus.ServiceBus) Events {
+ return &event{
+ bus: bus,
+ }
+}
+
+// On pass through
+func (r *event) On(eventName string, callback func(optionalData ...interface{})) {
+ eventMessage := &message.OnEventMessage{
+ Name: eventName,
+ Callback: callback,
+ }
+ r.bus.Publish("event:on", eventMessage)
+}
+
+// Emit pass through
+func (r *event) Emit(eventName string, optionalData ...interface{}) {
+ eventMessage := &message.EventMessage{
+ Name: eventName,
+ Data: optionalData,
+ }
+
+ r.bus.Publish("event:emit:from:g", eventMessage)
+}
diff --git a/v2/internal/runtime/goruntime/runtime.go b/v2/internal/runtime/goruntime/runtime.go
new file mode 100644
index 000000000..5b03ce4ba
--- /dev/null
+++ b/v2/internal/runtime/goruntime/runtime.go
@@ -0,0 +1,28 @@
+package goruntime
+
+import "github.com/wailsapp/wails/v2/internal/servicebus"
+
+// Runtime is a means for the user to interact with the application at runtime
+type Runtime struct {
+ Browser Browser
+ Events Events
+ Window Window
+ Dialog Dialog
+ bus *servicebus.ServiceBus
+}
+
+// New creates a new runtime
+func New(serviceBus *servicebus.ServiceBus) *Runtime {
+ return &Runtime{
+ Browser: newBrowser(),
+ Events: newEvents(serviceBus),
+ Window: newWindow(serviceBus),
+ Dialog: newDialog(serviceBus),
+ bus: serviceBus,
+ }
+}
+
+// Quit the application
+func (r *Runtime) Quit() {
+ r.bus.Publish("quit", "runtime.Quit()")
+}
diff --git a/v2/internal/runtime/goruntime/window.go b/v2/internal/runtime/goruntime/window.go
new file mode 100644
index 000000000..e7e4c06c5
--- /dev/null
+++ b/v2/internal/runtime/goruntime/window.go
@@ -0,0 +1,102 @@
+package goruntime
+
+import (
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Window defines all Window related operations
+type Window interface {
+ Close()
+ Show()
+ Hide()
+ Maximise()
+ Unmaximise()
+ Minimise()
+ Unminimise()
+ SetTitle(title string)
+ Fullscreen()
+ UnFullscreen()
+ SetColour(colour string)
+}
+
+// Window exposes the Windows interface
+type window struct {
+ bus *servicebus.ServiceBus
+}
+
+// newWindow creates a new window struct
+func newWindow(bus *servicebus.ServiceBus) Window {
+ return &window{
+ bus: bus,
+ }
+}
+
+// Close the Window
+// DISCUSSION:
+// Should we even be doing this now we have a server build?
+// Runtime.Quit() makes more sense than closing a window...
+func (w *window) Close() {
+ w.bus.Publish("quit", "runtime.Close()")
+}
+
+// SetTitle sets the title of the window
+func (w *window) SetTitle(title string) {
+ w.bus.Publish("window:settitle", title)
+}
+
+// Fullscreen makes the window fullscreen
+func (w *window) Fullscreen() {
+ w.bus.Publish("window:fullscreen", "")
+}
+
+// UnFullscreen makes the window UnFullscreen
+func (w *window) UnFullscreen() {
+ w.bus.Publish("window:unfullscreen", "")
+}
+
+// SetColour sets the window colour to the given string
+func (w *window) SetColour(colour string) {
+ w.bus.Publish("window:setcolour", colour)
+}
+
+// Show shows the window if hidden
+func (w *window) Show() {
+ w.bus.Publish("window:show", "")
+}
+
+// Hide the window
+func (w *window) Hide() {
+ w.bus.Publish("window:hide", "")
+}
+
+// SetSize sets the size of the window
+func (w *window) SetSize(width int, height int) {
+ size := []int{width, height}
+ w.bus.Publish("window:setsize", size)
+}
+
+// SetPosition sets the position of the window
+func (w *window) SetPosition(x int, y int) {
+ position := []int{x, y}
+ w.bus.Publish("window:position", position)
+}
+
+// Maximise the window
+func (w *window) Maximise() {
+ w.bus.Publish("window:maximise", "")
+}
+
+// Unmaximise the window
+func (w *window) Unmaximise() {
+ w.bus.Publish("window:unmaximise", "")
+}
+
+// Minimise the window
+func (w *window) Minimise() {
+ w.bus.Publish("window:minimise", "")
+}
+
+// Unminimise the window
+func (w *window) Unminimise() {
+ w.bus.Publish("window:unminimise", "")
+}
diff --git a/v2/internal/runtime/js/.eslintrc b/v2/internal/runtime/js/.eslintrc
new file mode 100644
index 000000000..2d112543b
--- /dev/null
+++ b/v2/internal/runtime/js/.eslintrc
@@ -0,0 +1,24 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "amd": true,
+ "node": true,
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaVersion": 2016,
+ "sourceType": "module",
+ },
+ "rules": {
+ "linebreak-style": 0,
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "semi": [
+ "error",
+ "always"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/v2/internal/runtime/js/babel.config.js b/v2/internal/runtime/js/babel.config.js
new file mode 100644
index 000000000..39d867cc4
--- /dev/null
+++ b/v2/internal/runtime/js/babel.config.js
@@ -0,0 +1,22 @@
+/* eslint-disable */
+
+module.exports = function (api) {
+ api.cache(true);
+
+ const presets = [
+ [
+ "@babel/preset-env",
+ {
+ "useBuiltIns": "entry",
+ "corejs": {
+ "version": 3,
+ "proposals": true
+ }
+ }
+ ]
+ ];
+
+ return {
+ presets,
+ };
+}
\ No newline at end of file
diff --git a/v2/internal/runtime/js/core/bindings.js b/v2/internal/runtime/js/core/bindings.js
new file mode 100644
index 000000000..f27a26b51
--- /dev/null
+++ b/v2/internal/runtime/js/core/bindings.js
@@ -0,0 +1,107 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+import { Call } from './calls';
+
+window.backend = {};
+
+/**
+
+Map of this format:
+
+{
+ packageName: {
+ structName: {
+ methodName: {
+ name: "",
+ inputs: [
+ {
+ type:
+ }
+ ],
+ outputs: [
+ {
+ type:
+ }
+ ]
+ }
+ }
+ }
+}
+ */
+
+export function SetBindings(bindingsMap) {
+ try {
+ bindingsMap = JSON.parse(bindingsMap);
+ } catch (e) {
+ console.error(e);
+ }
+
+ // Initialise the backend map
+ window.backend = window.backend || {};
+
+ // Iterate package names
+ Object.keys(bindingsMap).forEach((packageName) => {
+
+ // Create inner map if it doesn't exist
+ window.backend[packageName] = window.backend[packageName] || {};
+
+ // Iterate struct names
+ Object.keys(bindingsMap[packageName]).forEach((structName) => {
+
+ // Create inner map if it doesn't exist
+ window.backend[packageName][structName] = window.backend[packageName][structName] || {};
+
+ Object.keys(bindingsMap[packageName][structName]).forEach((methodName) => {
+
+ window.backend[packageName][structName][methodName] = function () {
+
+ // No timeout by default
+ var timeout = 0;
+
+ // Actual function
+ function dynamic() {
+ var args = [].slice.call(arguments);
+ return Call([packageName, structName, methodName].join('.'), args, timeout);
+ }
+
+ // Allow setting timeout to function
+ dynamic.setTimeout = function (newTimeout) {
+ timeout = newTimeout;
+ };
+
+ // Allow getting timeout to function
+ dynamic.getTimeout = function () {
+ return timeout;
+ };
+
+ return dynamic;
+ }();
+ });
+ });
+ });
+}
+// /**
+// * Determines if the given identifier is valid Javascript
+// *
+// * @param {boolean} name
+// * @returns
+// */
+// function isValidIdentifier(name) {
+// // Don't xss yourself :-)
+// try {
+// new Function('var ' + name);
+// return true;
+// } catch (e) {
+// return false;
+// }
+// }
+
diff --git a/v2/internal/runtime/js/core/browser.js b/v2/internal/runtime/js/core/browser.js
new file mode 100644
index 000000000..46c4e16be
--- /dev/null
+++ b/v2/internal/runtime/js/core/browser.js
@@ -0,0 +1,34 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+import { SendMessage } from 'ipc';
+
+/**
+ * Opens the given URL in the system browser
+ *
+ * @export
+ * @param {string} url
+ * @returns
+ */
+export function OpenURL(url) {
+ return SendMessage('RBU' + url);
+}
+
+/**
+ * Opens the given filename using the system's default file handler
+ *
+ * @export
+ * @param {sting} filename
+ * @returns
+ */
+export function OpenFile(filename) {
+ return SendMessage('runtime:browser:openfile', filename);
+}
diff --git a/v2/internal/runtime/js/core/calls.js b/v2/internal/runtime/js/core/calls.js
new file mode 100644
index 000000000..28b6278e2
--- /dev/null
+++ b/v2/internal/runtime/js/core/calls.js
@@ -0,0 +1,156 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+import { Debug } from './log';
+import { SendMessage } from 'ipc';
+
+var callbacks = {};
+
+/**
+ * Returns a number from the native browser random function
+ *
+ * @returns number
+ */
+function cryptoRandom() {
+ var array = new Uint32Array(1);
+ return window.crypto.getRandomValues(array)[0];
+}
+
+/**
+ * Returns a number using da old-skool Math.Random
+ * I likes to call it LOLRandom
+ *
+ * @returns number
+ */
+function basicRandom() {
+ return Math.random() * 9007199254740991;
+}
+
+// Pick a random number function based on browser capability
+var randomFunc;
+if (window.crypto) {
+ randomFunc = cryptoRandom;
+} else {
+ randomFunc = basicRandom;
+}
+
+
+/**
+ * Call sends a message to the backend to call the binding with the
+ * given data. A promise is returned and will be completed when the
+ * backend responds. This will be resolved when the call was successful
+ * or rejected if an error is passed back.
+ * There is a timeout mechanism. If the call doesn't respond in the given
+ * time (in milliseconds) then the promise is rejected.
+ *
+ * @export
+ * @param {string} name
+ * @param {string} args
+ * @param {number=} timeout
+ * @returns
+ */
+export function Call(name, args, timeout) {
+
+ // Timeout infinite by default
+ if (timeout == null || timeout == undefined) {
+ timeout = 0;
+ }
+
+ // Create a promise
+ return new Promise(function (resolve, reject) {
+
+ // Create a unique callbackID
+ var callbackID;
+ do {
+ callbackID = name + '-' + randomFunc();
+ } while (callbacks[callbackID]);
+
+ // Set timeout
+ if (timeout > 0) {
+ var timeoutHandle = setTimeout(function () {
+ reject(Error('Call to ' + name + ' timed out. Request ID: ' + callbackID));
+ }, timeout);
+ }
+
+ // Store callback
+ callbacks[callbackID] = {
+ timeoutHandle: timeoutHandle,
+ reject: reject,
+ resolve: resolve
+ };
+
+ try {
+ const payload = {
+ name,
+ args,
+ callbackID,
+ };
+
+ // Make the call
+ SendMessage('C' + JSON.stringify(payload));
+ } catch (e) {
+ // eslint-disable-next-line
+ console.error(e);
+ }
+ });
+}
+
+
+
+/**
+ * Called by the backend to return data to a previously called
+ * binding invocation
+ *
+ * @export
+ * @param {string} incomingMessage
+ */
+export function Callback(incomingMessage) {
+ // Decode the message - Credit: https://stackoverflow.com/a/13865680
+ //incomingMessage = decodeURIComponent(incomingMessage.replace(/\s+/g, '').replace(/[0-9a-f]{2}/g, '%$&'));
+
+ // Parse the message
+ var message;
+ try {
+ message = JSON.parse(incomingMessage);
+ } catch (e) {
+ const error = `Invalid JSON passed to callback: ${e.message}. Message: ${incomingMessage}`;
+ Debug(error);
+ throw new Error(error);
+ }
+ var callbackID = message.callbackid;
+ var callbackData = callbacks[callbackID];
+ if (!callbackData) {
+ const error = `Callback '${callbackID}' not registered!!!`;
+ console.error(error); // eslint-disable-line
+ throw new Error(error);
+ }
+ clearTimeout(callbackData.timeoutHandle);
+
+ delete callbacks[callbackID];
+
+ if (message.error) {
+ callbackData.reject(message.error);
+ } else {
+ callbackData.resolve(message.result);
+ }
+}
+
+/**
+ * SystemCall is used to call wails methods from the frontend
+ *
+ * @export
+ * @param {string} method
+ * @param {any[]=} data
+ * @returns
+ */
+export function SystemCall(method, data) {
+ return Call('.wails.' + method, data);
+}
diff --git a/v2/internal/runtime/js/core/desktop.js b/v2/internal/runtime/js/core/desktop.js
new file mode 100644
index 000000000..8238e44a6
--- /dev/null
+++ b/v2/internal/runtime/js/core/desktop.js
@@ -0,0 +1,35 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+import { SetBindings } from './bindings';
+import { Init } from './main';
+
+// Setup global error handler
+window.onerror = function (/*msg, url, lineNo, columnNo, error*/) {
+ // window.wails.Log.Error('**** Caught Unhandled Error ****');
+ // window.wails.Log.Error('Message: ' + msg);
+ // window.wails.Log.Error('URL: ' + url);
+ // window.wails.Log.Error('Line No: ' + lineNo);
+ // window.wails.Log.Error('Column No: ' + columnNo);
+ // window.wails.Log.Error('error: ' + error);
+ (function () { window.wails.Log.Error(new Error().stack); })();
+};
+
+// Initialise the Runtime
+Init();
+
+// Load Bindings if they exist
+if (window.wailsbindings) {
+ SetBindings(window.wailsbindings);
+}
+
+// Emit loaded event. Leaving this for now. It will show any errors if runtime fails to load.
+window.wails.Events.Emit('wails:loaded');
+
diff --git a/v2/internal/runtime/js/core/events.js b/v2/internal/runtime/js/core/events.js
new file mode 100644
index 000000000..a0dd2b1c2
--- /dev/null
+++ b/v2/internal/runtime/js/core/events.js
@@ -0,0 +1,202 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+import { Error } from './log';
+import { SendMessage } from 'ipc';
+
+// Defines a single listener with a maximum number of times to callback
+/**
+ * The Listener class defines a listener! :-)
+ *
+ * @class Listener
+ */
+class Listener {
+ /**
+ * Creates an instance of Listener.
+ * @param {function} callback
+ * @param {number} maxCallbacks
+ * @memberof Listener
+ */
+ constructor(callback, maxCallbacks) {
+ // Default of -1 means infinite
+ maxCallbacks = maxCallbacks || -1;
+ // Callback invokes the callback with the given data
+ // Returns true if this listener should be destroyed
+ this.Callback = (data) => {
+ callback.apply(null, data);
+ // If maxCallbacks is infinite, return false (do not destroy)
+ if (maxCallbacks === -1) {
+ return false;
+ }
+ // Decrement maxCallbacks. Return true if now 0, otherwise false
+ maxCallbacks -= 1;
+ return maxCallbacks === 0;
+ };
+ }
+}
+
+var eventListeners = {};
+
+/**
+ * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
+ *
+ * @export
+ * @param {string} eventName
+ * @param {function} callback
+ * @param {number} maxCallbacks
+ */
+export function OnMultiple(eventName, callback, maxCallbacks) {
+ eventListeners[eventName] = eventListeners[eventName] || [];
+ const thisListener = new Listener(callback, maxCallbacks);
+ console.log('Pushing event listener: ' + eventName);
+ eventListeners[eventName].push(thisListener);
+}
+
+/**
+ * Registers an event listener that will be invoked every time the event is emitted
+ *
+ * @export
+ * @param {string} eventName
+ * @param {function} callback
+ */
+export function On(eventName, callback) {
+ OnMultiple(eventName, callback);
+}
+
+/**
+ * Registers an event listener that will be invoked once then destroyed
+ *
+ * @export
+ * @param {string} eventName
+ * @param {function} callback
+ */
+export function Once(eventName, callback) {
+ OnMultiple(eventName, callback, 1);
+}
+
+/**
+ * Notify informs frontend listeners that an event was emitted with the given data
+ *
+ * @export
+ * @param {string} encoded notification message
+
+ */
+export function Notify(notifyMessage) {
+
+ // Parse the message
+ var message;
+ try {
+ message = JSON.parse(notifyMessage);
+ } catch (e) {
+ const error = 'Invalid JSON passed to Notify: ' + notifyMessage;
+ throw new Error(error);
+ }
+
+ var eventName = message.name;
+
+ // Check if we have any listeners for this event
+ if (eventListeners[eventName]) {
+
+ // Keep a list of listener indexes to destroy
+ const newEventListenerList = eventListeners[eventName].slice();
+
+ // Iterate listeners
+ for (let count = 0; count < eventListeners[eventName].length; count += 1) {
+
+ // Get next listener
+ const listener = eventListeners[eventName][count];
+
+ var data = message.data;
+
+ // Do the callback
+ const destroy = listener.Callback(data);
+ if (destroy) {
+ // if the listener indicated to destroy itself, add it to the destroy list
+ newEventListenerList.splice(count, 1);
+ }
+ }
+
+ // Update callbacks with new list of listners
+ eventListeners[eventName] = newEventListenerList;
+ }
+}
+
+/**
+ * Emit an event with the given name and data
+ *
+ * @export
+ * @param {string} eventName
+ */
+export function Emit(eventName) {
+
+ // Calculate the data
+ if (arguments.length > 1) {
+ // Notify backend
+ const payload = {
+ name: eventName,
+ data: [].slice.apply(arguments).slice(1),
+ };
+ SendMessage('Ej' + JSON.stringify(payload));
+ } else {
+ SendMessage('ej' + eventName);
+ }
+
+}
+
+// Callbacks for the heartbeat calls
+const heartbeatCallbacks = {};
+
+/**
+ * Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
+ * the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
+ *
+ * @export
+ * @param {string} eventName
+ * @param {number} timeInMilliseconds
+ * @param {function} callback
+ */
+export function Heartbeat(eventName, timeInMilliseconds, callback) {
+
+ // Declare interval variable
+ let interval = null;
+
+ // Setup callback
+ function dynamicCallback() {
+ // Kill interval
+ clearInterval(interval);
+ // Callback
+ callback();
+ }
+
+ // Register callback
+ heartbeatCallbacks[eventName] = dynamicCallback;
+
+ // Start emitting the event
+ interval = setInterval(function () {
+ Emit(eventName);
+ }, timeInMilliseconds);
+}
+
+/**
+ * Acknowledges a heartbeat event by name
+ *
+ * @export
+ * @param {string} eventName
+ */
+export function Acknowledge(eventName) {
+ // If we are waiting for acknowledgement for this event type
+ if (heartbeatCallbacks[eventName]) {
+ // Acknowledge!
+ heartbeatCallbacks[eventName]();
+ } else {
+ throw new Error(`Cannot acknowledge unknown heartbeat '${eventName}'`);
+ }
+}
diff --git a/v2/internal/runtime/js/core/log.js b/v2/internal/runtime/js/core/log.js
new file mode 100644
index 000000000..72f9fb684
--- /dev/null
+++ b/v2/internal/runtime/js/core/log.js
@@ -0,0 +1,76 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+
+/* jshint esversion: 6 */
+
+import { SendMessage } from 'ipc';
+
+/**
+ * Sends a log message to the backend with the given level + message
+ *
+ * @param {string} level
+ * @param {string} message
+ */
+function sendLogMessage(level, message) {
+
+ // Log Message format:
+ // l[type][message]
+ SendMessage('L' + level + message);
+}
+
+/**
+ * Log the given debug message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+export function Debug(message) {
+ sendLogMessage('D', message);
+}
+
+/**
+ * Log the given info message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+export function Info(message) {
+ sendLogMessage('I', message);
+}
+
+/**
+ * Log the given warning message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+export function Warning(message) {
+ sendLogMessage('W', message);
+}
+
+/**
+ * Log the given error message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+export function Error(message) {
+ sendLogMessage('E', message);
+}
+
+/**
+ * Log the given fatal message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+export function Fatal(message) {
+ sendLogMessage('F', message);
+}
diff --git a/v2/internal/runtime/js/core/main.js b/v2/internal/runtime/js/core/main.js
new file mode 100644
index 000000000..fa1a4862a
--- /dev/null
+++ b/v2/internal/runtime/js/core/main.js
@@ -0,0 +1,49 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+import * as Log from './log';
+import * as Browser from './browser';
+import * as Window from './window';
+import { On, OnMultiple, Emit, Notify, Heartbeat, Acknowledge } from './events';
+import { Callback } from './calls';
+import { AddScript, InjectCSS } from './utils';
+import { AddIPCListener } from 'ipc';
+import * as Platform from 'platform';
+
+export function Init() {
+ // Backend is where the Go struct wrappers get bound to
+ window.backend = {};
+
+ // Initialise global if not already
+ window.wails = {
+ System: Platform.System,
+ Log,
+ Browser,
+ Window,
+ Events: {
+ On,
+ OnMultiple,
+ Emit,
+ Heartbeat,
+ Acknowledge,
+ },
+ _: {
+ Callback,
+ Notify,
+ AddScript,
+ InjectCSS,
+ Init,
+ AddIPCListener
+ }
+ };
+
+ // Do platform specific Init
+ Platform.Init();
+}
\ No newline at end of file
diff --git a/v2/internal/runtime/js/core/server.js b/v2/internal/runtime/js/core/server.js
new file mode 100644
index 000000000..30cd1820a
--- /dev/null
+++ b/v2/internal/runtime/js/core/server.js
@@ -0,0 +1,182 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+import { Init } from './main';
+import { SetBindings } from './bindings';
+
+function init() {
+ // Bridge object
+ window.wailsbridge = {
+ reconnectOverlay: null,
+ reconnectTimer: 300,
+ wsURL: 'ws://' + window.location.hostname + ':8080/ws',
+ connectionState: null,
+ config: {},
+ websocket: null,
+ callback: null,
+ overlayHTML:
+ 'Wails Bridge
Waiting for backend
',
+ overlayCSS:
+ '.wails-reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);font-family:sans-serif;display:none;z-index:999999}.wails-reconnect-overlay-content{padding:20px 30px;text-align:center;width:20em;position:relative;height:14em;border-radius:1em;margin:5% auto 0;background-color:#fff;box-shadow:1px 1px 20px 3px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAqFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAEBAQAAAAAAAAAAAAEBAQEBAQDAwMBAQEAAAABAQEAAAAAAAAAAAABAQEAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAAAAAABAQEAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWKCj6oAAAAN3RSTlMALiIqDhkGBAswJjP0GxP6NR4W9/ztjRDMhWU50G9g5eHXvbZ9XEI9xZTcqZl2aldKo55QwoCvZUgzhAAAAs9JREFUSMeNleeWqjAUhU0BCaH3Itiw9zKT93+zG02QK1hm/5HF+jzZJ6fQe6cyXE+jg9X7o9wxuylIIf4Tv2V3+bOrEXnf8dwQ/KQIGDN2/S+4OmVCVXL/ScBnfibxURqIByP/hONE8r8T+bDMlQ98KSl7Y8hzjpS8v1qtDh8u5f8KQpGpfnPPhqG8JeogN37Hq9eaN2xRhIwAaGnvws8F1ShxqK5ob2twYi1FAMD4rXsYtnC/JEiRbl4cUrCWhnMCLRFemXezXbb59QK4WASOsm6n2W1+4CBT2JmtzQ6fsrbGubR/NFbd2g5Y179+5w/GEHaKsHjYCet7CgrXU3txarNC7YxOVJtIj4/ERzMdZfzc31hp+8cD6eGILgarZY9uZ12hAs03vfBD9C171gS5Omz7OcvxALQIn4u8RRBBBcsi9WW2woO9ipLgfzpYlggg3ZRdROUC8KT7QLqq3W9KB5BbdFVg4929kdwp6+qaZnMCCNBdj+NyN1W885Ry/AL3D4AQbsVV4noCiM/C83kyYq80XlDAYQtralOiDzoRAHlotWl8q2tjvYlOgcg1A8jEApZa+C06TBdAz2Qv0wu11I/zZOyJQ6EwGez2P2b8PIQr1hwwnAZsAxwA4UAYOyXUxM/xp6tHAn4GUmPGM9R28oVxgC0e/zQJJI6DyhyZ1r7uzRQhpcW7x7vTaWSzKSG6aep77kroTEl3U81uSVaUTtgEINfC8epx+Q4F9SpplHG84Ek6m4RAq9/TLkOBrxyeuddZhHvGIp1XXfFy3Z3vtwNblKGiDn+J+92vwwABHghj7HnzlS1H5kB49AZvdGCFgiBPq69qfXPr3y++yilF0ON4R8eR7spAsLpZ95NqAW5tab1c4vkZm6aleajchMwYTdILQQTwE2OV411ZM9WztDjPql12caBi6gDpUKmDd4U1XNdQxZ4LIXQ5/Tr4P7I9tYcFrDK3AAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center}.wails-reconnect-overlay-title{font-size:2em}.wails-reconnect-overlay-message{font-size:1.3em}.wails-reconnect-overlay-loadingspinner{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#3E67EC #eee #eee;border-radius:50%;animation:loadingspin 1s linear infinite;margin:auto;padding:2.5em}@keyframes loadingspin{100%{transform:rotate(360deg)}}',
+ log: function (message) {
+ // eslint-disable-next-line
+ console.log(
+ '%c wails bridge %c ' + message + ' ',
+ 'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
+ 'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
+ );
+ }
+ };
+}
+
+// Adapted from webview - thanks zserge!
+function injectCSS(css) {
+ var elem = document.createElement('style');
+ elem.setAttribute('type', 'text/css');
+ if (elem.styleSheet) {
+ elem.styleSheet.cssText = css;
+ } else {
+ elem.appendChild(document.createTextNode(css));
+ }
+ var head = document.head || document.getElementsByTagName('head')[0];
+ head.appendChild(elem);
+}
+
+// Creates a node in the Dom
+function createNode(parent, elementType, id, className, content) {
+ var d = document.createElement(elementType);
+ if (id) {
+ d.id = id;
+ }
+ if (className) {
+ d.className = className;
+ }
+ if (content) {
+ d.innerHTML = content;
+ }
+ parent.appendChild(d);
+ return d;
+}
+
+// Sets up the overlay
+function setupOverlay() {
+ var body = document.body;
+
+ var wailsBridgeNode = createNode(body, 'div', 'wails-bridge');
+ wailsBridgeNode.innerHTML = window.wailsbridge.overlayHTML;
+
+ // Inject the overlay CSS
+ injectCSS(window.wailsbridge.overlayCSS);
+}
+
+// Start the Wails Bridge
+function startBridge() {
+ // Setup the overlay
+ setupOverlay();
+
+ window.wailsbridge.websocket = null;
+ window.wailsbridge.connectTimer = null;
+ window.wailsbridge.reconnectOverlay = document.querySelector(
+ '.wails-reconnect-overlay'
+ );
+ window.wailsbridge.connectionState = 'disconnected';
+
+ // Shows the overlay
+ function showReconnectOverlay() {
+ window.wailsbridge.reconnectOverlay.style.display = 'block';
+ }
+
+ // Hides the overlay
+ function hideReconnectOverlay() {
+ window.wailsbridge.reconnectOverlay.style.display = 'none';
+ }
+
+ // Handles incoming websocket connections
+ function handleConnect() {
+ window.wailsbridge.log('Connected to backend');
+ hideReconnectOverlay();
+ clearInterval(window.wailsbridge.connectTimer);
+ window.wailsbridge.websocket.onclose = handleDisconnect;
+ window.wailsbridge.websocket.onmessage = handleMessage;
+ window.wailsbridge.connectionState = 'connected';
+ }
+
+ // Handles websocket disconnects
+ function handleDisconnect() {
+ window.wailsbridge.log('Disconnected from backend');
+ window.wailsbridge.websocket = null;
+ window.wailsbridge.connectionState = 'disconnected';
+ showReconnectOverlay();
+ connect();
+ }
+
+ // Try to connect to the backend every 300ms (default value).
+ // Change this value in the main wailsbridge object.
+ function connect() {
+ window.wailsbridge.connectTimer = setInterval(function () {
+ if (window.wailsbridge.websocket == null) {
+ window.wailsbridge.websocket = new WebSocket(window.wailsbridge.wsURL);
+ window.wailsbridge.websocket.onopen = handleConnect;
+ window.wailsbridge.websocket.onerror = function (e) {
+ e.stopImmediatePropagation();
+ e.stopPropagation();
+ e.preventDefault();
+ window.wailsbridge.websocket = null;
+ return false;
+ };
+ }
+ }, window.wailsbridge.reconnectTimer);
+ }
+
+ function handleMessage(message) {
+ switch (message.data[0]) {
+ case 'e':
+ case 'E':
+ window.wails._.Notify(message.data.slice(1));
+ break;
+ case 'R':
+ window.wails._.Callback(message.data.slice(1));
+ break;
+ default:
+ window.wails.Log.Error('Unknown message type received: ' + message.data[0]);
+ }
+ }
+
+ // Start by showing the overlay...
+ showReconnectOverlay();
+
+ // ...and attempt to connect
+ connect();
+}
+
+function start() {
+
+ // Set up the bridge
+ init();
+
+ // Start Bridge
+ startBridge();
+
+ // Load bindings
+ window.wailspreinit = function () {
+ if (window.wailsbindings) {
+ SetBindings(window.wailsbindings);
+ }
+ };
+
+ Init();
+
+ // Save the binding script
+ window.SetBindings = SetBindings;
+}
+
+// Start your engines!
+start();
diff --git a/v2/internal/runtime/js/core/utils.js b/v2/internal/runtime/js/core/utils.js
new file mode 100644
index 000000000..89ef1d7d6
--- /dev/null
+++ b/v2/internal/runtime/js/core/utils.js
@@ -0,0 +1,38 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+import { Emit } from './events';
+
+export function AddScript(js, callbackID) {
+ var script = document.createElement('script');
+ script.text = js;
+ document.body.appendChild(script);
+ if (callbackID) {
+ Emit(callbackID);
+ }
+}
+
+// Adapted from webview - thanks zserge!
+export function InjectCSS(css) {
+ try {
+ var elem = document.createElement('style');
+ elem.setAttribute('type', 'text/css');
+ if (elem.styleSheet) {
+ elem.styleSheet.cssText = css;
+ } else {
+ elem.appendChild(document.createTextNode(css));
+ }
+ var head = document.head || document.getElementsByTagName('head')[0];
+ head.appendChild(elem);
+ } catch (e) {
+ console.log(e);
+ }
+}
diff --git a/v2/internal/runtime/js/core/window.js b/v2/internal/runtime/js/core/window.js
new file mode 100644
index 000000000..e634c992d
--- /dev/null
+++ b/v2/internal/runtime/js/core/window.js
@@ -0,0 +1,107 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+
+/* jshint esversion: 6 */
+
+import { SendMessage } from 'ipc';
+
+/**
+ * Place the window in the center of the screen
+ *
+ * @export
+ */
+export function Center() {
+ SendMessage('Wc');
+}
+
+/**
+ * Set the Size of the window
+ *
+ * @export
+ * @param {number} width
+ * @param {number} height
+ */
+export function SetSize(width, height) {
+ SendMessage('Ws:' + width + ':' + height);
+}
+
+/**
+ * Set the Position of the window
+ *
+ * @export
+ * @param {number} x
+ * @param {number} y
+ */
+export function SetPosition(x, y) {
+ SendMessage('Wp:' + x + ':' + y);
+}
+
+/**
+ * Hide the Window
+ *
+ * @export
+ */
+export function Hide() {
+ SendMessage('WH');
+}
+
+/**
+ * Show the Window
+ *
+ * @export
+ */
+export function Show() {
+ SendMessage('WS');
+}
+
+/**
+ * Maximise the Window
+ *
+ * @export
+ */
+export function Maximise() {
+ SendMessage('WM');
+}
+
+/**
+ * Unmaximise the Window
+ *
+ * @export
+ */
+export function Unmaximise() {
+ SendMessage('WU');
+}
+
+/**
+ * Minimise the Window
+ *
+ * @export
+ */
+export function Minimise() {
+ SendMessage('Wm');
+}
+
+/**
+ * Unminimise the Window
+ *
+ * @export
+ */
+export function Unminimise() {
+ SendMessage('Wu');
+}
+
+/**
+ * Close the Window
+ *
+ * @export
+ */
+export function Close() {
+ SendMessage('WC');
+}
diff --git a/v2/internal/runtime/js/desktop/ipc.js b/v2/internal/runtime/js/desktop/ipc.js
new file mode 100644
index 000000000..40f553559
--- /dev/null
+++ b/v2/internal/runtime/js/desktop/ipc.js
@@ -0,0 +1,41 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+import * as Platform from 'platform';
+
+// IPC Listeners
+var listeners = [];
+
+/**
+ * Adds a listener to IPC messages
+ * @param {function} callback
+ */
+export function AddIPCListener(callback) {
+ listeners.push(callback);
+}
+
+/**
+ * SendMessage sends the given message to the backend
+ *
+ * @param {string} message
+ */
+export function SendMessage(message) {
+
+ // Call Platform specific invoke method
+ Platform.SendMessage(message);
+
+ // Also send to listeners
+ if (listeners.length > 0) {
+ for (var i = 0; i < listeners.length; i++) {
+ listeners[i](message);
+ }
+ }
+}
diff --git a/v2/internal/runtime/js/desktop/linux.js b/v2/internal/runtime/js/desktop/linux.js
new file mode 100644
index 000000000..95c0d15ef
--- /dev/null
+++ b/v2/internal/runtime/js/desktop/linux.js
@@ -0,0 +1,41 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+/**
+ * Initialises platform specific code
+ */
+
+export const System = {
+ Platform: "linux",
+ AppType: "desktop"
+}
+
+export function SendMessage(message) {
+ window.webkit.messageHandlers.external.postMessage(message);
+}
+
+export function Init() {
+
+ // Setup drag handler
+ // Based on code from: https://github.com/patr0nus/DeskGap
+ window.addEventListener('mousedown', function (e) {
+ var currentElement = e.target;
+ while (currentElement != null) {
+ if (currentElement.hasAttribute('data-wails-no-drag')) {
+ break;
+ } else if (currentElement.hasAttribute('data-wails-drag')) {
+ window.webkit.messageHandlers.windowDrag.postMessage(null);
+ break;
+ }
+ currentElement = currentElement.parentElement;
+ }
+ });
+}
\ No newline at end of file
diff --git a/v2/internal/runtime/js/package-lock.json b/v2/internal/runtime/js/package-lock.json
new file mode 100644
index 000000000..4a073126f
--- /dev/null
+++ b/v2/internal/runtime/js/package-lock.json
@@ -0,0 +1,5988 @@
+{
+ "name": "wails-runtime",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/cli": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.8.4.tgz",
+ "integrity": "sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==",
+ "dev": true,
+ "requires": {
+ "chokidar": "^2.1.8",
+ "commander": "^4.0.1",
+ "convert-source-map": "^1.1.0",
+ "fs-readdir-recursive": "^1.1.0",
+ "glob": "^7.0.0",
+ "lodash": "^4.17.13",
+ "make-dir": "^2.1.0",
+ "slash": "^2.0.0",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz",
+ "integrity": "sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.9.1",
+ "invariant": "^2.2.4",
+ "semver": "^5.5.0"
+ }
+ },
+ "@babel/core": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz",
+ "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.9.0",
+ "@babel/helper-module-transforms": "^7.9.0",
+ "@babel/helpers": "^7.9.0",
+ "@babel/parser": "^7.9.0",
+ "@babel/template": "^7.8.6",
+ "@babel/traverse": "^7.9.0",
+ "@babel/types": "^7.9.0",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.1",
+ "json5": "^2.1.2",
+ "lodash": "^4.17.13",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz",
+ "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.9.5",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.13",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz",
+ "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz",
+ "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-explode-assignable-expression": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.8.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz",
+ "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.8.6",
+ "browserslist": "^4.9.1",
+ "invariant": "^2.2.4",
+ "levenary": "^1.1.1",
+ "semver": "^5.5.0"
+ }
+ },
+ "@babel/helper-create-regexp-features-plugin": {
+ "version": "7.8.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz",
+ "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.8.3",
+ "@babel/helper-regex": "^7.8.3",
+ "regexpu-core": "^4.7.0"
+ }
+ },
+ "@babel/helper-define-map": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz",
+ "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.8.3",
+ "@babel/types": "^7.8.3",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-explode-assignable-expression": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz",
+ "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
+ "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.9.5"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
+ "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz",
+ "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
+ "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
+ "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz",
+ "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.8.6",
+ "@babel/helper-simple-access": "^7.8.3",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/template": "^7.8.6",
+ "@babel/types": "^7.9.0",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
+ "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
+ "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
+ "dev": true
+ },
+ "@babel/helper-regex": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz",
+ "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz",
+ "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.8.3",
+ "@babel/helper-wrap-function": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz",
+ "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.8.3",
+ "@babel/helper-optimise-call-expression": "^7.8.3",
+ "@babel/traverse": "^7.8.6",
+ "@babel/types": "^7.8.6"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz",
+ "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
+ "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+ "dev": true
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz",
+ "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.9.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz",
+ "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.9.0",
+ "@babel/types": "^7.9.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.9.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
+ "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
+ "dev": true
+ },
+ "@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz",
+ "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-remap-async-to-generator": "^7.8.3",
+ "@babel/plugin-syntax-async-generators": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-dynamic-import": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz",
+ "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz",
+ "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-numeric-separator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz",
+ "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz",
+ "integrity": "sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+ "@babel/plugin-transform-parameters": "^7.9.5"
+ }
+ },
+ "@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-optional-chaining": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz",
+ "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.8.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz",
+ "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.8.8",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-dynamic-import": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+ "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-numeric-separator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz",
+ "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-top-level-await": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz",
+ "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-arrow-functions": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz",
+ "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz",
+ "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-remap-async-to-generator": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz",
+ "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz",
+ "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz",
+ "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.8.3",
+ "@babel/helper-define-map": "^7.8.3",
+ "@babel/helper-function-name": "^7.9.5",
+ "@babel/helper-optimise-call-expression": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.8.6",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz",
+ "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz",
+ "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-dotall-regex": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz",
+ "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz",
+ "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz",
+ "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz",
+ "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz",
+ "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz",
+ "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-member-expression-literals": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz",
+ "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz",
+ "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.9.0",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "babel-plugin-dynamic-import-node": "^2.3.0"
+ }
+ },
+ "@babel/plugin-transform-modules-commonjs": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz",
+ "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.9.0",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-simple-access": "^7.8.3",
+ "babel-plugin-dynamic-import-node": "^2.3.0"
+ }
+ },
+ "@babel/plugin-transform-modules-systemjs": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz",
+ "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.8.3",
+ "@babel/helper-module-transforms": "^7.9.0",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "babel-plugin-dynamic-import-node": "^2.3.0"
+ }
+ },
+ "@babel/plugin-transform-modules-umd": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz",
+ "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.9.0",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz",
+ "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-new-target": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz",
+ "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-object-assign": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.8.3.tgz",
+ "integrity": "sha512-i3LuN8tPDqUCRFu3dkzF2r1Nx0jp4scxtm7JxtIqI9he9Vk20YD+/zshdzR9JLsoBMlJlNR82a62vQExNEVx/Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz",
+ "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz",
+ "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-property-literals": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz",
+ "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.8.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz",
+ "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "^0.14.2"
+ }
+ },
+ "@babel/plugin-transform-reserved-words": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz",
+ "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz",
+ "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz",
+ "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz",
+ "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-regex": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz",
+ "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz",
+ "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz",
+ "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.5.tgz",
+ "integrity": "sha512-eWGYeADTlPJH+wq1F0wNfPbVS1w1wtmMJiYk55Td5Yu28AsdR9AsC97sZ0Qq8fHqQuslVSIYSGJMcblr345GfQ==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.9.0",
+ "@babel/helper-compilation-targets": "^7.8.7",
+ "@babel/helper-module-imports": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-proposal-async-generator-functions": "^7.8.3",
+ "@babel/plugin-proposal-dynamic-import": "^7.8.3",
+ "@babel/plugin-proposal-json-strings": "^7.8.3",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-proposal-numeric-separator": "^7.8.3",
+ "@babel/plugin-proposal-object-rest-spread": "^7.9.5",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-proposal-optional-chaining": "^7.9.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.8.3",
+ "@babel/plugin-syntax-async-generators": "^7.8.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.0",
+ "@babel/plugin-syntax-json-strings": "^7.8.0",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0",
+ "@babel/plugin-syntax-numeric-separator": "^7.8.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.0",
+ "@babel/plugin-syntax-top-level-await": "^7.8.3",
+ "@babel/plugin-transform-arrow-functions": "^7.8.3",
+ "@babel/plugin-transform-async-to-generator": "^7.8.3",
+ "@babel/plugin-transform-block-scoped-functions": "^7.8.3",
+ "@babel/plugin-transform-block-scoping": "^7.8.3",
+ "@babel/plugin-transform-classes": "^7.9.5",
+ "@babel/plugin-transform-computed-properties": "^7.8.3",
+ "@babel/plugin-transform-destructuring": "^7.9.5",
+ "@babel/plugin-transform-dotall-regex": "^7.8.3",
+ "@babel/plugin-transform-duplicate-keys": "^7.8.3",
+ "@babel/plugin-transform-exponentiation-operator": "^7.8.3",
+ "@babel/plugin-transform-for-of": "^7.9.0",
+ "@babel/plugin-transform-function-name": "^7.8.3",
+ "@babel/plugin-transform-literals": "^7.8.3",
+ "@babel/plugin-transform-member-expression-literals": "^7.8.3",
+ "@babel/plugin-transform-modules-amd": "^7.9.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.9.0",
+ "@babel/plugin-transform-modules-systemjs": "^7.9.0",
+ "@babel/plugin-transform-modules-umd": "^7.9.0",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3",
+ "@babel/plugin-transform-new-target": "^7.8.3",
+ "@babel/plugin-transform-object-super": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.9.5",
+ "@babel/plugin-transform-property-literals": "^7.8.3",
+ "@babel/plugin-transform-regenerator": "^7.8.7",
+ "@babel/plugin-transform-reserved-words": "^7.8.3",
+ "@babel/plugin-transform-shorthand-properties": "^7.8.3",
+ "@babel/plugin-transform-spread": "^7.8.3",
+ "@babel/plugin-transform-sticky-regex": "^7.8.3",
+ "@babel/plugin-transform-template-literals": "^7.8.3",
+ "@babel/plugin-transform-typeof-symbol": "^7.8.4",
+ "@babel/plugin-transform-unicode-regex": "^7.8.3",
+ "@babel/preset-modules": "^0.1.3",
+ "@babel/types": "^7.9.5",
+ "browserslist": "^4.9.1",
+ "core-js-compat": "^3.6.2",
+ "invariant": "^2.2.2",
+ "levenary": "^1.1.1",
+ "semver": "^5.5.0"
+ }
+ },
+ "@babel/preset-modules": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz",
+ "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+ "@babel/plugin-transform-dotall-regex": "^7.4.4",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.9.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
+ "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "@babel/template": {
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz",
+ "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.9.5",
+ "@babel/helper-function-name": "^7.9.5",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/parser": "^7.9.0",
+ "@babel/types": "^7.9.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.13"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
+ "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.5",
+ "lodash": "^4.17.13",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+ "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
+ "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+ "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+ "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-code-frame": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
+ "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/wast-printer": "1.9.0"
+ }
+ },
+ "@webassemblyjs/helper-fsm": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
+ "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-module-context": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
+ "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0"
+ }
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+ "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+ "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+ "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
+ "dev": true,
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+ "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
+ "dev": true,
+ "requires": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+ "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
+ "dev": true
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+ "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/helper-wasm-section": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-opt": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "@webassemblyjs/wast-printer": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+ "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+ "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+ "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wast-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
+ "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/floating-point-hex-parser": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-code-frame": "1.9.0",
+ "@webassemblyjs/helper-fsm": "1.9.0",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+ "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
+ "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
+ "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
+ "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-errors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+ "dev": true
+ },
+ "ajv-keywords": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
+ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
+ "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.11.0"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
+ "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
+ "dev": true
+ }
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "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"
+ },
+ "dependencies": {
+ "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"
+ }
+ }
+ }
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "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.5.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+ "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.1.1",
+ "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
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "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
+ },
+ "babel-helper-evaluate-path": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.5.0.tgz",
+ "integrity": "sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==",
+ "dev": true
+ },
+ "babel-helper-flip-expressions": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz",
+ "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=",
+ "dev": true
+ },
+ "babel-helper-is-nodes-equiv": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz",
+ "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=",
+ "dev": true
+ },
+ "babel-helper-is-void-0": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz",
+ "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=",
+ "dev": true
+ },
+ "babel-helper-mark-eval-scopes": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz",
+ "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=",
+ "dev": true
+ },
+ "babel-helper-remove-or-void": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz",
+ "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=",
+ "dev": true
+ },
+ "babel-helper-to-multiple-sequence-expressions": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz",
+ "integrity": "sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA==",
+ "dev": true
+ },
+ "babel-loader": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz",
+ "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==",
+ "dev": true,
+ "requires": {
+ "find-cache-dir": "^2.1.0",
+ "loader-utils": "^1.4.0",
+ "mkdirp": "^0.5.3",
+ "pify": "^4.0.1",
+ "schema-utils": "^2.6.5"
+ }
+ },
+ "babel-plugin-dynamic-import-node": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz",
+ "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==",
+ "dev": true,
+ "requires": {
+ "object.assign": "^4.1.0"
+ }
+ },
+ "babel-plugin-minify-builtins": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.5.0.tgz",
+ "integrity": "sha512-wpqbN7Ov5hsNwGdzuzvFcjgRlzbIeVv1gMIlICbPj0xkexnfoIDe7q+AZHMkQmAE/F9R5jkrB6TLfTegImlXag==",
+ "dev": true
+ },
+ "babel-plugin-minify-constant-folding": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.5.0.tgz",
+ "integrity": "sha512-Vj97CTn/lE9hR1D+jKUeHfNy+m1baNiJ1wJvoGyOBUx7F7kJqDZxr9nCHjO/Ad+irbR3HzR6jABpSSA29QsrXQ==",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.5.0"
+ }
+ },
+ "babel-plugin-minify-dead-code-elimination": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.5.1.tgz",
+ "integrity": "sha512-x8OJOZIrRmQBcSqxBcLbMIK8uPmTvNWPXH2bh5MDCW1latEqYiRMuUkPImKcfpo59pTUB2FT7HfcgtG8ZlR5Qg==",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.5.0",
+ "babel-helper-mark-eval-scopes": "^0.4.3",
+ "babel-helper-remove-or-void": "^0.4.3",
+ "lodash": "^4.17.11"
+ }
+ },
+ "babel-plugin-minify-flip-comparisons": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz",
+ "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=",
+ "dev": true,
+ "requires": {
+ "babel-helper-is-void-0": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-guarded-expressions": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.4.tgz",
+ "integrity": "sha512-RMv0tM72YuPPfLT9QLr3ix9nwUIq+sHT6z8Iu3sLbqldzC1Dls8DPCywzUIzkTx9Zh1hWX4q/m9BPoPed9GOfA==",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.5.0",
+ "babel-helper-flip-expressions": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-infinity": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz",
+ "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=",
+ "dev": true
+ },
+ "babel-plugin-minify-mangle-names": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.5.0.tgz",
+ "integrity": "sha512-3jdNv6hCAw6fsX1p2wBGPfWuK69sfOjfd3zjUXkbq8McbohWy23tpXfy5RnToYWggvqzuMOwlId1PhyHOfgnGw==",
+ "dev": true,
+ "requires": {
+ "babel-helper-mark-eval-scopes": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-numeric-literals": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz",
+ "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=",
+ "dev": true
+ },
+ "babel-plugin-minify-replace": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.5.0.tgz",
+ "integrity": "sha512-aXZiaqWDNUbyNNNpWs/8NyST+oU7QTpK7J9zFEFSA0eOmtUNMU3fczlTTTlnCxHmq/jYNFEmkkSG3DDBtW3Y4Q==",
+ "dev": true
+ },
+ "babel-plugin-minify-simplify": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.5.1.tgz",
+ "integrity": "sha512-OSYDSnoCxP2cYDMk9gxNAed6uJDiDz65zgL6h8d3tm8qXIagWGMLWhqysT6DY3Vs7Fgq7YUDcjOomhVUb+xX6A==",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.5.0",
+ "babel-helper-flip-expressions": "^0.4.3",
+ "babel-helper-is-nodes-equiv": "^0.0.1",
+ "babel-helper-to-multiple-sequence-expressions": "^0.5.0"
+ }
+ },
+ "babel-plugin-minify-type-constructors": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz",
+ "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=",
+ "dev": true,
+ "requires": {
+ "babel-helper-is-void-0": "^0.4.3"
+ }
+ },
+ "babel-plugin-transform-inline-consecutive-adds": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz",
+ "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=",
+ "dev": true
+ },
+ "babel-plugin-transform-member-expression-literals": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz",
+ "integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=",
+ "dev": true
+ },
+ "babel-plugin-transform-merge-sibling-variables": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz",
+ "integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=",
+ "dev": true
+ },
+ "babel-plugin-transform-minify-booleans": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz",
+ "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=",
+ "dev": true
+ },
+ "babel-plugin-transform-property-literals": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz",
+ "integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "babel-plugin-transform-regexp-constructors": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz",
+ "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=",
+ "dev": true
+ },
+ "babel-plugin-transform-remove-console": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz",
+ "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=",
+ "dev": true
+ },
+ "babel-plugin-transform-remove-debugger": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz",
+ "integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=",
+ "dev": true
+ },
+ "babel-plugin-transform-remove-undefined": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.5.0.tgz",
+ "integrity": "sha512-+M7fJYFaEE/M9CXa0/IRkDbiV3wRELzA1kKQFCJ4ifhrzLKn/9VCCgj9OFmYWwBd8IB48YdgPkHYtbYq+4vtHQ==",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.5.0"
+ }
+ },
+ "babel-plugin-transform-simplify-comparison-operators": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz",
+ "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=",
+ "dev": true
+ },
+ "babel-plugin-transform-undefined-to-void": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz",
+ "integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=",
+ "dev": true
+ },
+ "babel-preset-minify": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.5.1.tgz",
+ "integrity": "sha512-1IajDumYOAPYImkHbrKeiN5AKKP9iOmRoO2IPbIuVp0j2iuCcj0n7P260z38siKMZZ+85d3mJZdtW8IgOv+Tzg==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-minify-builtins": "^0.5.0",
+ "babel-plugin-minify-constant-folding": "^0.5.0",
+ "babel-plugin-minify-dead-code-elimination": "^0.5.1",
+ "babel-plugin-minify-flip-comparisons": "^0.4.3",
+ "babel-plugin-minify-guarded-expressions": "^0.4.4",
+ "babel-plugin-minify-infinity": "^0.4.3",
+ "babel-plugin-minify-mangle-names": "^0.5.0",
+ "babel-plugin-minify-numeric-literals": "^0.4.3",
+ "babel-plugin-minify-replace": "^0.5.0",
+ "babel-plugin-minify-simplify": "^0.5.1",
+ "babel-plugin-minify-type-constructors": "^0.4.3",
+ "babel-plugin-transform-inline-consecutive-adds": "^0.4.3",
+ "babel-plugin-transform-member-expression-literals": "^6.9.4",
+ "babel-plugin-transform-merge-sibling-variables": "^6.9.4",
+ "babel-plugin-transform-minify-booleans": "^6.9.4",
+ "babel-plugin-transform-property-literals": "^6.9.4",
+ "babel-plugin-transform-regexp-constructors": "^0.4.3",
+ "babel-plugin-transform-remove-console": "^6.9.4",
+ "babel-plugin-transform-remove-debugger": "^6.9.4",
+ "babel-plugin-transform-remove-undefined": "^0.5.0",
+ "babel-plugin-transform-simplify-comparison-operators": "^6.9.4",
+ "babel-plugin-transform-undefined-to-void": "^6.9.4",
+ "lodash": "^4.17.11"
+ }
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
+ "dev": true
+ },
+ "big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "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"
+ }
+ },
+ "browserslist": {
+ "version": "4.11.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz",
+ "integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001038",
+ "electron-to-chromium": "^1.3.390",
+ "node-releases": "^1.1.53",
+ "pkg-up": "^2.0.0"
+ }
+ },
+ "buffer": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+ "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+ "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": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "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"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001040",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001040.tgz",
+ "integrity": "sha512-Ep0tEPeI5wCvmJNrXjE3etgfI+lkl1fTDU6Y3ZH1mhrjkPlVI9W4pcKbMo+BQLpEWKVYYp2EmYaRsqpPC3k7lQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ },
+ "dependencies": {
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ }
+ }
+ }
+ },
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "chrome-trace-event": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
+ "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
+ "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": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.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": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "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
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ }
+ }
+ },
+ "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": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "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.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "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.2.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+ "dev": true
+ },
+ "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
+ },
+ "convert-source-map": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "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-js": {
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
+ "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
+ "dev": true
+ },
+ "core-js-compat": {
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz",
+ "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.8.5",
+ "semver": "7.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+ "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+ "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": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+ "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": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "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
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+ "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "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.7.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.3.402",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.402.tgz",
+ "integrity": "sha512-gaCDfX7IUH0s3JmBiHCDPrvVcdnTTP1r4WLJc2dHkYYbLmXZ2XHiJCcGQ9Balf91aKTvuCKCyu2JjJYRykoI1w==",
+ "dev": true
+ },
+ "elliptic": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
+ "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
+ "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"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "enhanced-resolve": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
+ "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.5.0",
+ "tapable": "^1.0.0"
+ },
+ "dependencies": {
+ "memory-fs": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+ "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ }
+ }
+ },
+ "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": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+ "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.10.0",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^1.4.3",
+ "eslint-visitor-keys": "^1.1.0",
+ "espree": "^6.1.2",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^7.0.0",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.14",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.3",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "semver": "^6.1.2",
+ "strip-ansi": "^5.2.0",
+ "strip-json-comments": "^3.0.1",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "12.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+ "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
+ "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+ "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+ "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+ "dev": true
+ },
+ "espree": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
+ "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.1.1",
+ "acorn-jsx": "^5.2.0",
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz",
+ "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.0.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz",
+ "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==",
+ "dev": true
+ }
+ }
+ },
+ "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.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "events": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
+ "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
+ "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": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.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"
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.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"
+ }
+ }
+ }
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "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": "3.1.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
+ "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "figgy-pudding": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
+ "dev": true
+ },
+ "figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": 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.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": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.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"
+ }
+ },
+ "findup-sync": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+ "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ }
+ },
+ "flatted": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "dev": true
+ },
+ "flush-write-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.3.6"
+ }
+ },
+ "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-readdir-recursive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+ "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+ "dev": true
+ },
+ "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
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "gensync": {
+ "version": "1.0.0-beta.1",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
+ "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==",
+ "dev": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "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.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "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": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+ "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^3.0.0"
+ },
+ "dependencies": {
+ "global-prefix": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+ "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "dev": true,
+ "requires": {
+ "ini": "^1.3.5",
+ "kind-of": "^6.0.2",
+ "which": "^1.3.1"
+ }
+ }
+ }
+ },
+ "global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "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-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "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.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "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"
+ }
+ },
+ "homedir-polyfill": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+ "dev": true,
+ "requires": {
+ "parse-passwd": "^1.0.0"
+ }
+ },
+ "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.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+ "dev": true
+ },
+ "iferr": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+ "dev": true
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+ "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "import-local": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+ "dev": true,
+ "requires": {
+ "pkg-dir": "^3.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
+ },
+ "infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "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.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz",
+ "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^3.0.0",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.15",
+ "mute-stream": "0.0.8",
+ "run-async": "^2.4.0",
+ "rxjs": "^6.5.3",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "interpret": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
+ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
+ "dev": true
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "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": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "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
+ },
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+ "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
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "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
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+ "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "dev": true,
+ "requires": {
+ "invert-kv": "^2.0.0"
+ }
+ },
+ "leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true
+ },
+ "levenary": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz",
+ "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==",
+ "dev": true,
+ "requires": {
+ "leven": "^3.1.0"
+ }
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "loader-runner": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+ "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.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.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ }
+ },
+ "map-age-cleaner": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+ "dev": true,
+ "requires": {
+ "p-defer": "^1.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.0"
+ }
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "mem": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
+ "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "^0.1.1",
+ "mimic-fn": "^2.0.0",
+ "p-is-promise": "^2.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": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "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": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "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": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "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.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "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.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
+ "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
+ "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"
+ }
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
+ "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.2.1",
+ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+ "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
+ "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": "^3.0.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "0.0.1",
+ "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.11.0",
+ "vm-browserify": "^1.0.1"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ }
+ }
+ },
+ "node-releases": {
+ "version": "1.1.53",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz",
+ "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "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-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "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.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "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": "5.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+ "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "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": "3.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "lcid": "^2.0.0",
+ "mem": "^4.0.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-defer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+ "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+ "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-is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
+ "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
+ "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.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "parallel-transform": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+ "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+ "dev": true,
+ "requires": {
+ "cyclist": "^1.0.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.1.5"
+ }
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-asn1": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
+ "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
+ "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",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+ "dev": true
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+ "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
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "pbkdf2": {
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "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": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.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.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "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.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ }
+ }
+ },
+ "pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "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
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "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.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "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
+ },
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "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",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "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"
+ },
+ "dependencies": {
+ "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"
+ }
+ }
+ }
+ },
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "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.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "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.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "regenerate": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
+ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
+ "dev": true
+ },
+ "regenerate-unicode-properties": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz",
+ "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+ "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "0.14.4",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz",
+ "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.8.4",
+ "private": "^0.1.8"
+ }
+ },
+ "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"
+ }
+ },
+ "regexpp": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+ "dev": true
+ },
+ "regexpu-core": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
+ "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0",
+ "regenerate-unicode-properties": "^8.2.0",
+ "regjsgen": "^0.5.1",
+ "regjsparser": "^0.6.4",
+ "unicode-match-property-ecmascript": "^1.0.4",
+ "unicode-match-property-value-ecmascript": "^1.2.0"
+ }
+ },
+ "regjsgen": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz",
+ "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz",
+ "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+ "dev": true
+ }
+ }
+ },
+ "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": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
+ "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "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-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ },
+ "dependencies": {
+ "global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ }
+ }
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "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": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.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.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "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.4.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
+ "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==",
+ "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.5.5",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz",
+ "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==",
+ "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": "2.6.5",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
+ "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.0",
+ "ajv-keywords": "^3.4.1"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "serialize-javascript": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
+ "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
+ "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-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "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.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+ "dev": true
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+ "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.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-support": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
+ "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "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
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "ssri": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.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.2",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+ "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.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.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": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ }
+ }
+ },
+ "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
+ },
+ "strip-json-comments": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz",
+ "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==",
+ "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"
+ }
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ },
+ "dependencies": {
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "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
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ }
+ }
+ },
+ "tapable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "dev": true
+ },
+ "terser": {
+ "version": "4.6.11",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.11.tgz",
+ "integrity": "sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "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
+ }
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
+ "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
+ "dev": true,
+ "requires": {
+ "cacache": "^12.0.2",
+ "find-cache-dir": "^2.1.0",
+ "is-wsl": "^1.1.0",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^2.1.2",
+ "source-map": "^0.6.1",
+ "terser": "^4.1.2",
+ "webpack-sources": "^1.4.0",
+ "worker-farm": "^1.7.0"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ },
+ "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
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "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.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "timers-browserify": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
+ "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
+ "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-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "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"
+ }
+ },
+ "tslib": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
+ "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
+ "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
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "unicode-canonical-property-names-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==",
+ "dev": true
+ },
+ "unicode-match-property-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
+ "dev": true,
+ "requires": {
+ "unicode-canonical-property-names-ecmascript": "^1.0.4",
+ "unicode-property-aliases-ecmascript": "^1.0.4"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz",
+ "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==",
+ "dev": true
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz",
+ "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==",
+ "dev": true
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ }
+ },
+ "unique-filename": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "dev": true,
+ "requires": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+ "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+ "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.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "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.11.1",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+ "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ }
+ }
+ },
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
+ "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
+ "dev": true
+ },
+ "vm-browserify": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+ "dev": true
+ },
+ "watchpack": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",
+ "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==",
+ "dev": true,
+ "requires": {
+ "chokidar": "^2.1.8",
+ "graceful-fs": "^4.1.2",
+ "neo-async": "^2.5.0"
+ }
+ },
+ "webpack": {
+ "version": "4.42.1",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz",
+ "integrity": "sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/wasm-edit": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "acorn": "^6.2.1",
+ "ajv": "^6.10.2",
+ "ajv-keywords": "^3.4.1",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^4.1.0",
+ "eslint-scope": "^4.0.3",
+ "json-parse-better-errors": "^1.0.2",
+ "loader-runner": "^2.4.0",
+ "loader-utils": "^1.2.3",
+ "memory-fs": "^0.4.1",
+ "micromatch": "^3.1.10",
+ "mkdirp": "^0.5.3",
+ "neo-async": "^2.6.1",
+ "node-libs-browser": "^2.2.1",
+ "schema-utils": "^1.0.0",
+ "tapable": "^1.1.3",
+ "terser-webpack-plugin": "^1.4.3",
+ "watchpack": "^1.6.0",
+ "webpack-sources": "^1.4.1"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+ "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
+ "dev": true
+ },
+ "eslint-scope": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+ "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ }
+ }
+ },
+ "webpack-cli": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz",
+ "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==",
+ "dev": true,
+ "requires": {
+ "chalk": "2.4.2",
+ "cross-spawn": "6.0.5",
+ "enhanced-resolve": "4.1.0",
+ "findup-sync": "3.0.0",
+ "global-modules": "2.0.0",
+ "import-local": "2.0.0",
+ "interpret": "1.2.0",
+ "loader-utils": "1.2.3",
+ "supports-color": "6.1.0",
+ "v8-compile-cache": "2.0.3",
+ "yargs": "13.2.4"
+ },
+ "dependencies": {
+ "emojis-list": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+ "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^2.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz",
+ "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "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
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "worker-farm": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+ "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+ "dev": true,
+ "requires": {
+ "errno": "~0.1.7"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "dependencies": {
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "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
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "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": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "13.2.4",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
+ "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "os-locale": "^3.1.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.0"
+ },
+ "dependencies": {
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "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.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "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.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+}
diff --git a/v2/internal/runtime/js/package.json b/v2/internal/runtime/js/package.json
new file mode 100644
index 000000000..0f7a9cdf3
--- /dev/null
+++ b/v2/internal/runtime/js/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "wails-runtime",
+ "version": "1.0.0",
+ "description": "The Javascript Wails Runtime",
+ "main": "index.js",
+ "scripts": {
+ "build:desktop": "./node_modules/.bin/eslint core/ && ./node_modules/.bin/webpack --env desktop --colors",
+ "build:server": "./node_modules/.bin/eslint core/ && ./node_modules/.bin/webpack --env server --colors",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/wailsapp/runtime.git"
+ },
+ "keywords": [
+ "Wails",
+ "Go",
+ "Javascript",
+ "Runtime"
+ ],
+ "browserslist": [
+ "> 5%",
+ "IE 9"
+ ],
+ "author": "Lea Anthony ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/wailsapp/runtime/issues"
+ },
+ "homepage": "https://github.com/wailsapp/runtime#readme",
+ "devDependencies": {
+ "@babel/cli": "^7.7.7",
+ "@babel/core": "^7.7.7",
+ "@babel/plugin-transform-object-assign": "^7.7.4",
+ "@babel/preset-env": "^7.7.7",
+ "babel-loader": "^8.0.6",
+ "babel-preset-minify": "^0.5.1",
+ "core-js": "^3.6.1",
+ "eslint": "^6.8.0",
+ "webpack": "^4.41.5",
+ "webpack-cli": "^3.3.10"
+ }
+}
diff --git a/v2/internal/runtime/js/package.json.md5 b/v2/internal/runtime/js/package.json.md5
new file mode 100644
index 000000000..7341680b1
--- /dev/null
+++ b/v2/internal/runtime/js/package.json.md5
@@ -0,0 +1 @@
+77a32d4461a2cad598edfd551fe64dcd
\ No newline at end of file
diff --git a/v2/internal/runtime/js/runtime/.npmignore b/v2/internal/runtime/js/runtime/.npmignore
new file mode 100644
index 000000000..d0a1d1fad
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/.npmignore
@@ -0,0 +1 @@
+bridge.js
\ No newline at end of file
diff --git a/v2/internal/runtime/js/runtime/README.md b/v2/internal/runtime/js/runtime/README.md
new file mode 100644
index 000000000..bb608fdca
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/README.md
@@ -0,0 +1,3 @@
+# Wails Runtime
+
+This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) project, not a standalone module.
diff --git a/v2/internal/runtime/js/runtime/browser.js b/v2/internal/runtime/js/runtime/browser.js
new file mode 100644
index 000000000..70167883f
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/browser.js
@@ -0,0 +1,37 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+/**
+ * Opens the given URL in the system browser
+ *
+ * @export
+ * @param {string} url
+ * @returns
+ */
+function OpenURL(url) {
+ return window.wails.Browser.OpenURL(url);
+}
+
+/**
+ * Opens the given filename using the system's default file handler
+ *
+ * @export
+ * @param {sting} filename
+ * @returns
+ */
+function OpenFile(filename) {
+ return window.wails.Browser.OpenFile(filename);
+}
+
+module.exports = {
+ OpenURL: OpenURL,
+ OpenFile: OpenFile
+};
\ No newline at end of file
diff --git a/v2/internal/runtime/js/runtime/events.js b/v2/internal/runtime/js/runtime/events.js
new file mode 100644
index 000000000..30a23a01e
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/events.js
@@ -0,0 +1,90 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+
+/**
+ * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
+ *
+ * @export
+ * @param {string} eventName
+ * @param {function} callback
+ * @param {number} maxCallbacks
+ */
+function OnMultiple(eventName, callback, maxCallbacks) {
+ window.wails.Events.OnMultiple(eventName, callback, maxCallbacks);
+}
+
+/**
+ * Registers an event listener that will be invoked every time the event is emitted
+ *
+ * @export
+ * @param {string} eventName
+ * @param {function} callback
+ */
+function On(eventName, callback) {
+ OnMultiple(eventName, callback);
+}
+
+/**
+ * Registers an event listener that will be invoked once then destroyed
+ *
+ * @export
+ * @param {string} eventName
+ * @param {function} callback
+ */
+function Once(eventName, callback) {
+ OnMultiple(eventName, callback, 1);
+}
+
+
+/**
+ * Emit an event with the given name and data
+ *
+ * @export
+ * @param {string} eventName
+ */
+function Emit(eventName) {
+ var args = [eventName].slice.call(arguments);
+ return window.wails.Events.Emit.apply(null, args);
+}
+
+
+/**
+ * Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
+ * the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
+ *
+ * @export
+ * @param {string} eventName
+ * @param {number} timeInMilliseconds
+ * @param {function} callback
+ */
+function Heartbeat(eventName, timeInMilliseconds, callback) {
+ window.wails.Events.Heartbeat(eventName, timeInMilliseconds, callback);
+}
+
+/**
+ * Acknowledges a heartbeat event by name
+ *
+ * @export
+ * @param {string} eventName
+ */
+function Acknowledge(eventName) {
+ return window.wails.Events.Acknowledge(eventName);
+}
+
+module.exports = {
+ OnMultiple: OnMultiple,
+ On: On,
+ Once: Once,
+ Emit: Emit,
+ Heartbeat: Heartbeat,
+ Acknowledge: Acknowledge
+};
\ No newline at end of file
diff --git a/v2/internal/runtime/js/runtime/init.js b/v2/internal/runtime/js/runtime/init.js
new file mode 100644
index 000000000..8ade99048
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/init.js
@@ -0,0 +1,21 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+/**
+ * Initialises the Wails runtime
+ *
+ * @param {function} callback
+ */
+function Init(callback) {
+ window.wails._.Init(callback);
+}
+
+module.exports = Init;
\ No newline at end of file
diff --git a/v2/internal/runtime/js/runtime/log.js b/v2/internal/runtime/js/runtime/log.js
new file mode 100644
index 000000000..2defed8f7
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/log.js
@@ -0,0 +1,70 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+
+/* jshint esversion: 6 */
+
+
+/**
+ * Log the given debug message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+function Debug(message) {
+ window.wails.Log.Debug(message);
+}
+
+/**
+ * Log the given info message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+function Info(message) {
+ window.wails.Log.Info(message);
+}
+
+/**
+ * Log the given warning message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+function Warning(message) {
+ window.wails.Log.Warning(message);
+}
+
+/**
+ * Log the given error message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+function Error(message) {
+ window.wails.Log.Error(message);
+}
+
+/**
+ * Log the given fatal message with the backend
+ *
+ * @export
+ * @param {string} message
+ */
+function Fatal(message) {
+ window.wails.Log.Fatal(message);
+}
+
+module.exports = {
+ Debug: Debug,
+ Info: Info,
+ Warning: Warning,
+ Error: Error,
+ Fatal: Fatal
+};
diff --git a/v2/internal/runtime/js/runtime/main.js b/v2/internal/runtime/js/runtime/main.js
new file mode 100644
index 000000000..82dcf6ee0
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/main.js
@@ -0,0 +1,22 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+const Log = require('./log');
+const Browser = require('./browser');
+const Events = require('./events');
+const Init = require('./init');
+
+module.exports = {
+ Log: Log,
+ Browser: Browser,
+ Events: Events,
+ Init: Init
+};
\ No newline at end of file
diff --git a/v2/internal/runtime/js/runtime/package.json b/v2/internal/runtime/js/runtime/package.json
new file mode 100644
index 000000000..2937d27ca
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@wailsapp/runtime",
+ "version": "1.0.10",
+ "description": "Wails Javascript runtime library",
+ "main": "main.js",
+ "types": "runtime.d.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/wailsapp/wails.git"
+ },
+ "keywords": [
+ "Wails",
+ "Javascript",
+ "Go"
+ ],
+ "author": "Lea Anthony ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/wailsapp/wails/issues"
+ },
+ "homepage": "https://github.com/wailsapp/wails#readme",
+ "devDependencies": {
+ "dts-gen": "^0.5.8"
+ }
+}
diff --git a/v2/internal/runtime/js/runtime/runtime.d.ts b/v2/internal/runtime/js/runtime/runtime.d.ts
new file mode 100644
index 000000000..7749942ed
--- /dev/null
+++ b/v2/internal/runtime/js/runtime/runtime.d.ts
@@ -0,0 +1,26 @@
+export = wailsapp__runtime;
+
+declare const wailsapp__runtime: {
+ Browser: {
+ OpenFile(filename: string): Promise;
+ OpenURL(url: string): Promise;
+ };
+ Events: {
+ Acknowledge(eventName: string): void;
+ Emit(eventName: string): void;
+ Heartbeat(eventName: string, timeInMilliseconds: number, callback: () => void): void;
+ On(eventName: string, callback: () => void): void;
+ OnMultiple(eventName: string, callback: () => void, maxCallbacks: number): void;
+ Once(eventName: string, callback: () => void): void;
+ };
+ Init(callback: () => void): void;
+ Log: {
+ Debug(message: string): void;
+ Error(message: string): void;
+ Fatal(message: string): void;
+ Info(message: string): void;
+ Warning(message: string): void;
+ };
+};
+
+
diff --git a/v2/internal/runtime/js/server/ipc.js b/v2/internal/runtime/js/server/ipc.js
new file mode 100644
index 000000000..b42713367
--- /dev/null
+++ b/v2/internal/runtime/js/server/ipc.js
@@ -0,0 +1,52 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+// IPC Listeners
+var listeners = [];
+
+/**
+ * Adds a listener to IPC messages
+ * @param {function} callback
+ */
+export function AddIPCListener(callback) {
+ listeners.push(callback);
+}
+
+/**
+ * Invoke sends the given message to the backend
+ *
+ * @param {string} message
+ */
+function Invoke(message) {
+ if (window.wailsbridge && window.wailsbridge.websocket) {
+ window.wailsbridge.websocket.send(JSON.stringify(message));
+ } else {
+ console.log('Invoke called with: ' + message + ' but no runtime is available');
+ }
+
+ // Also send to listeners
+ if (listeners.length > 0) {
+ for (var i = 0; i < listeners.length; i++) {
+ listeners[i](message);
+ }
+ }
+}
+
+/**
+ * Sends a message to the backend based on the given type, payload and callbackID
+ *
+ * @export
+ * @param {string} message
+ */
+
+export function SendMessage(message) {
+ Invoke(message);
+}
diff --git a/v2/internal/runtime/js/server/linux.js b/v2/internal/runtime/js/server/linux.js
new file mode 100644
index 000000000..17b7c8cd7
--- /dev/null
+++ b/v2/internal/runtime/js/server/linux.js
@@ -0,0 +1,21 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The lightweight framework for web-like apps
+(c) Lea Anthony 2019-present
+*/
+/* jshint esversion: 6 */
+
+/**
+ * Initialises platform specific code
+ */
+
+export const System = {
+ Platform: "linux",
+ AppType: "server"
+}
+
+export function Init() { }
\ No newline at end of file
diff --git a/v2/internal/runtime/js/webpack.config.js b/v2/internal/runtime/js/webpack.config.js
new file mode 100644
index 000000000..62cb81a20
--- /dev/null
+++ b/v2/internal/runtime/js/webpack.config.js
@@ -0,0 +1,4 @@
+/* eslint-disable */
+module.exports = (env) => {
+ return require(`./webpack.${env}.js`);
+};
\ No newline at end of file
diff --git a/v2/internal/runtime/js/webpack.desktop.js b/v2/internal/runtime/js/webpack.desktop.js
new file mode 100644
index 000000000..067e0f353
--- /dev/null
+++ b/v2/internal/runtime/js/webpack.desktop.js
@@ -0,0 +1,51 @@
+/* eslint-disable */
+
+const path = require('path');
+
+const platform = process.env.WAILSPLATFORM;
+if (!platform) {
+ console.error("FATAL: Environment variable WAILSPLATFORM not set!");
+ process.exit(1);
+}
+
+module.exports = {
+ entry: './core/desktop',
+ mode: 'production',
+ output: {
+ path: path.resolve(__dirname, '..', 'assets'),
+ filename: 'desktop.js',
+ library: 'Wails'
+ },
+ resolve: {
+ alias: {
+ ipc$: path.resolve(__dirname, 'desktop/ipc.js'),
+ platform$: path.resolve(__dirname, `desktop/${platform}.js`)
+ }
+ },
+ module: {
+ rules: [
+ {
+ test: /\.m?js$/,
+ exclude: /(node_modules|bower_components)/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ plugins: ['@babel/plugin-transform-object-assign'],
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ 'useBuiltIns': 'entry',
+ 'corejs': {
+ 'version': 3,
+ 'proposals': true
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+};
diff --git a/v2/internal/runtime/js/webpack.server.js b/v2/internal/runtime/js/webpack.server.js
new file mode 100644
index 000000000..58ed73b67
--- /dev/null
+++ b/v2/internal/runtime/js/webpack.server.js
@@ -0,0 +1,54 @@
+/* eslint-disable */
+
+const path = require('path');
+
+const platform = process.env.WAILSPLATFORM;
+if (!platform) {
+ console.error("FATAL: Environment variable WAILSPLATFORM not set!");
+ process.exit(1);
+}
+
+module.exports = {
+ entry: './core/server',
+ mode: 'production',
+ output: {
+ path: path.resolve(__dirname, '..', 'assets'),
+ filename: 'server.js',
+ library: 'Wails'
+ },
+ resolve: {
+ alias: {
+ ipc$: path.resolve(__dirname, 'server/ipc.js'),
+ platform$: path.resolve(__dirname, `server/${platform}.js`)
+ }
+ },
+ module: {
+ rules: [
+ {
+ test: /\.m?js$/,
+ include: [
+ path.resolve(__dirname, "server"),
+ ],
+ exclude: /(node_modules|bower_components)/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ plugins: ['@babel/plugin-transform-object-assign'],
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ 'useBuiltIns': 'entry',
+ 'corejs': {
+ 'version': 3,
+ 'proposals': true
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+};
diff --git a/v2/internal/servicebus/message.go b/v2/internal/servicebus/message.go
new file mode 100644
index 000000000..aea95f5e3
--- /dev/null
+++ b/v2/internal/servicebus/message.go
@@ -0,0 +1,43 @@
+package servicebus
+
+// Message is a service bus message that contains a
+// topic and data
+type Message struct {
+ topic string
+ data interface{}
+ target string
+}
+
+// NewMessage creates a new message with the given
+// topic and data
+func NewMessage(topic string, data interface{}) *Message {
+ return &Message{
+ topic: topic,
+ data: data,
+ }
+}
+
+// NewMessageForTarget creates a new message with the given
+// topic and data
+func NewMessageForTarget(topic string, data interface{}, target string) *Message {
+ return &Message{
+ topic: topic,
+ data: data,
+ target: target,
+ }
+}
+
+// Topic returns the message topic
+func (m *Message) Topic() string {
+ return m.topic
+}
+
+// Data returns the message data
+func (m *Message) Data() interface{} {
+ return m.data
+}
+
+// Target returns the message Target
+func (m *Message) Target() string {
+ return m.target
+}
diff --git a/v2/internal/servicebus/servicebus.go b/v2/internal/servicebus/servicebus.go
new file mode 100644
index 000000000..55c37480e
--- /dev/null
+++ b/v2/internal/servicebus/servicebus.go
@@ -0,0 +1,192 @@
+package servicebus
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+)
+
+// ServiceBus is a messaging bus for Wails applications
+type ServiceBus struct {
+ listeners map[string][]chan *Message
+ messageQueue chan *Message
+ quitChannel chan struct{}
+ wg sync.WaitGroup
+ lock sync.RWMutex
+ closed bool
+ debug bool
+ logger logger.CustomLogger
+}
+
+// New creates a new ServiceBus
+// The internal message queue is set to 100 messages
+// Listener queues are set to 10
+func New(logger *logger.Logger) *ServiceBus {
+ return &ServiceBus{
+ listeners: make(map[string][]chan *Message),
+ messageQueue: make(chan *Message, 100),
+ quitChannel: make(chan struct{}, 1),
+ logger: logger.CustomLogger("Service Bus"),
+ }
+}
+
+// dispatch the given message to the listeners
+func (s *ServiceBus) dispatchMessage(message *Message) {
+
+ // Lock to prevent additions to the listeners
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+
+ // Iterate over listener's topics
+ for topic := range s.listeners {
+
+ // If the topic matches
+ if strings.HasPrefix(message.Topic(), topic) {
+
+ // Iterate over the listeners
+ for _, callback := range s.listeners[topic] {
+
+ // Process the message
+ callback <- message
+ }
+ }
+ }
+}
+
+// Debug puts the service bus into debug mode.
+func (s *ServiceBus) Debug() {
+ s.debug = true
+}
+
+// Start the service bus
+func (s *ServiceBus) Start() error {
+
+ s.logger.Trace("Starting")
+
+ // Prevent starting when closed
+ if s.closed {
+ return fmt.Errorf("cannot call start on closed servicebus")
+ }
+
+ // We run in a different thread
+ go func() {
+
+ quit := false
+ s.wg.Add(1)
+
+ // Loop until we get a quit message
+ for !quit {
+
+ select {
+
+ // Listen for messages
+ case message := <-s.messageQueue:
+
+ // Log message if in debug mode
+ if s.debug {
+ s.logger.Trace("Got message: { Topic: %s, Interface: %#v }", message.Topic(), message.Data())
+ }
+ // Dispatch message
+ s.dispatchMessage(message)
+
+ // Listen for quit messages
+ case <-s.quitChannel:
+ quit = true
+ }
+ }
+
+ // Indicate we have shut down
+ s.wg.Done()
+
+ }()
+
+ return nil
+}
+
+// Stop the service bus
+func (s *ServiceBus) Stop() error {
+
+ // Prevent subscribing when closed
+ if s.closed {
+ return fmt.Errorf("cannot call stop on closed servicebus")
+ }
+
+ s.closed = true
+
+ // Send quit message
+ s.quitChannel <- struct{}{}
+
+ // Wait for dispatcher to stop
+ s.wg.Wait()
+
+ // Close down subscriber channels
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ for _, subscribers := range s.listeners {
+ for _, channel := range subscribers {
+ close(channel)
+ }
+ }
+
+ // Close message queue
+ close(s.messageQueue)
+
+ s.logger.Trace("Stopped")
+ return nil
+}
+
+// UnSubscribe removes the listeners for the given topic (Use with caution!)
+func (s *ServiceBus) UnSubscribe(topic string) {
+ // Prevent any reads or writes to the listeners whilst
+ // we create a new one
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.listeners[topic] = nil
+}
+
+// Subscribe is used to register a listener's interest in a topic
+func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) {
+
+ // Prevent subscribing when closed
+ if s.closed {
+ return nil, fmt.Errorf("cannot call subscribe on closed servicebus")
+ }
+
+ // Prevent any reads or writes to the listeners whilst
+ // we create a new one
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ // Append the new listener
+ listener := make(chan *Message, 10)
+ s.listeners[topic] = append(s.listeners[topic], listener)
+ return (<-chan *Message)(listener), nil
+
+}
+
+// Publish sends the given message on the service bus
+func (s *ServiceBus) Publish(topic string, data interface{}) error {
+ // Prevent publish when closed
+ if s.closed {
+ return fmt.Errorf("cannot call publish on closed servicebus")
+ }
+
+ message := NewMessage(topic, data)
+ s.messageQueue <- message
+ return nil
+}
+
+// PublishForTarget sends the given message on the service bus for the given target
+func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) error {
+ // Prevent publish when closed
+ if s.closed {
+ return fmt.Errorf("cannot call publish on closed servicebus")
+ }
+
+ message := NewMessageForTarget(topic, data, target)
+ s.messageQueue <- message
+ return nil
+}
diff --git a/v2/internal/servicebus/servicebus_test.go b/v2/internal/servicebus/servicebus_test.go
new file mode 100644
index 000000000..750a56aaa
--- /dev/null
+++ b/v2/internal/servicebus/servicebus_test.go
@@ -0,0 +1,230 @@
+package servicebus
+
+import (
+ "sync"
+ "testing"
+
+ "github.com/matryer/is"
+ "github.com/wailsapp/wails/v2/internal/logger"
+)
+
+type Person interface {
+ FullName() string
+}
+
+type person struct {
+ Firstname string
+ Lastname string
+}
+
+func newPerson(firstname string, lastname string) *person {
+ result := &person{}
+ result.Firstname = firstname
+ result.Lastname = lastname
+ return result
+}
+
+func (p *person) FullName() string {
+ return p.Firstname + " " + p.Lastname
+}
+
+func TestSingleTopic(t *testing.T) {
+
+ is := is.New(t)
+
+ var expected string = "I am a message!"
+ var actual string
+
+ var wg sync.WaitGroup
+
+ // Create new bus
+ bus := New(logger.New())
+ messageChannel, _ := bus.Subscribe("hello")
+
+ wg.Add(1)
+ go func() {
+ message := <-messageChannel
+ actual = message.Data().(string)
+ wg.Done()
+ }()
+
+ bus.Start()
+ bus.Publish("hello", "I am a message!")
+ wg.Wait()
+ bus.Stop()
+
+ is.Equal(actual, expected)
+
+}
+func TestMultipleTopics(t *testing.T) {
+
+ is := is.New(t)
+
+ var hello string
+ var world string
+ var expected string = "Hello World!"
+
+ var wg sync.WaitGroup
+
+ // Create new bus
+ bus := New(logger.New())
+
+ // Create subscriptions
+ helloChannel, _ := bus.Subscribe("hello")
+ worldChannel, _ := bus.Subscribe("world")
+
+ wg.Add(1)
+ go func() {
+ counter := 2
+ for counter > 0 {
+ select {
+ case helloMessage := <-helloChannel:
+ hello = helloMessage.Data().(string)
+ counter--
+ case worldMessage := <-worldChannel:
+ world = worldMessage.Data().(string)
+ counter--
+ }
+ }
+ wg.Done()
+ }()
+
+ bus.Start()
+ bus.Publish("hello", "Hello ")
+ bus.Publish("world", "World!")
+ wg.Wait()
+ bus.Stop()
+
+ is.Equal(hello+world, expected)
+}
+
+func TestSingleTopicWildcard(t *testing.T) {
+
+ is := is.New(t)
+
+ var expected string = "I am a message!"
+ var actual string
+
+ var wg sync.WaitGroup
+
+ // Create new bus
+ bus := New(logger.New())
+ messageChannel, _ := bus.Subscribe("hello")
+
+ wg.Add(1)
+ go func() {
+ message := <-messageChannel
+ actual = message.Data().(string)
+ wg.Done()
+ }()
+
+ bus.Start()
+ bus.Publish("hello:wildcard:test", "I am a message!")
+ wg.Wait()
+ bus.Stop()
+
+ is.Equal(actual, expected)
+
+}
+func TestMultipleTopicsWildcard(t *testing.T) {
+
+ is := is.New(t)
+
+ var hello string
+ var world string
+ var expected string = "Hello World!"
+
+ var wg sync.WaitGroup
+
+ // Create new bus
+ bus := New(logger.New())
+ helloChannel, _ := bus.Subscribe("hello")
+ worldChannel, _ := bus.Subscribe("world")
+
+ wg.Add(1)
+ go func() {
+ counter := 2
+ for counter > 0 {
+ select {
+ case helloMessage := <-helloChannel:
+ hello = helloMessage.Data().(string)
+ counter--
+ case worldMessage := <-worldChannel:
+ world = worldMessage.Data().(string)
+ counter--
+ }
+ }
+ wg.Done()
+ }()
+
+ bus.Start()
+ bus.Publish("hello:wildcard:test", "Hello ")
+ bus.Publish("world:wildcard:test", "World!")
+ wg.Wait()
+ bus.Stop()
+
+ is.Equal(hello+world, expected)
+}
+
+func TestStructData(t *testing.T) {
+
+ is := is.New(t)
+
+ var expected string = "Tom Jones"
+ var actual string
+
+ var wg sync.WaitGroup
+
+ // Create new bus
+ bus := New(logger.New())
+ messageChannel, _ := bus.Subscribe("person")
+
+ wg.Add(1)
+ go func() {
+ message := <-messageChannel
+ p := message.Data().(*person)
+ actual = p.FullName()
+ wg.Done()
+ }()
+
+ bus.Start()
+ bus.Publish("person", newPerson("Tom", "Jones"))
+ wg.Wait()
+ bus.Stop()
+
+ is.Equal(actual, expected)
+
+}
+
+func TestErrors(t *testing.T) {
+
+ is := is.New(t)
+
+ // Create new bus
+ bus := New(logger.New())
+
+ _, err := bus.Subscribe("person")
+ is.NoErr(err)
+
+ err = bus.Start()
+ is.NoErr(err)
+
+ err = bus.Publish("person", newPerson("Tom", "Jones"))
+ is.NoErr(err)
+
+ err = bus.Stop()
+ is.NoErr(err)
+
+ err = bus.Stop()
+ is.True(err != nil)
+
+ err = bus.Start()
+ is.True(err != nil)
+
+ _, err = bus.Subscribe("person")
+ is.True(err != nil)
+
+ err = bus.Publish("person", newPerson("Tom", "Jones"))
+ is.True(err != nil)
+
+}
diff --git a/v2/internal/shell/shell.go b/v2/internal/shell/shell.go
new file mode 100644
index 000000000..8c36836fb
--- /dev/null
+++ b/v2/internal/shell/shell.go
@@ -0,0 +1,90 @@
+package shell
+
+import (
+ "bytes"
+ "os"
+ "os/exec"
+)
+
+type Command struct {
+ command string
+ args []string
+ env []string
+ dir string
+ stdo, stde bytes.Buffer
+}
+
+func NewCommand(command string) *Command {
+ return &Command{
+ command: command,
+ env: os.Environ(),
+ }
+}
+
+func (c *Command) Dir(dir string) {
+ c.dir = dir
+}
+
+func (c *Command) Env(name string, value string) {
+ c.env = append(c.env, name+"="+value)
+}
+
+func (c *Command) Run() error {
+ cmd := exec.Command(c.command, c.args...)
+ if c.dir != "" {
+ cmd.Dir = c.dir
+ }
+ cmd.Stdout = &c.stdo
+ cmd.Stderr = &c.stde
+ return cmd.Run()
+}
+
+func (c *Command) Stdout() string {
+ return c.stdo.String()
+}
+func (c *Command) Stderr() string {
+ return c.stde.String()
+}
+
+func (c *Command) AddArgs(args []string) {
+ for _, arg := range args {
+ c.args = append(c.args, arg)
+ }
+}
+
+// CreateCommand returns a *Cmd struct that when run, will run the given command + args in the given directory
+func CreateCommand(directory string, command string, args ...string) *exec.Cmd {
+ cmd := exec.Command(command, args...)
+ cmd.Dir = directory
+ return cmd
+}
+
+// RunCommand will run the given command + args in the given directory
+// Will return stdout, stderr and error
+func RunCommand(directory string, command string, args ...string) (string, string, error) {
+ cmd := CreateCommand(directory, command, args...)
+ var stdo, stde bytes.Buffer
+ cmd.Stdout = &stdo
+ cmd.Stderr = &stde
+ err := cmd.Run()
+ return stdo.String(), stde.String(), err
+}
+
+// RunCommandVerbose will run the given command + args in the given directory
+// Will return an error if one occurs
+func RunCommandVerbose(directory string, command string, args ...string) error {
+ cmd := CreateCommand(directory, command, args...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ return err
+}
+
+// CommandExists returns true if the given command can be found on the shell
+func CommandExists(name string) bool {
+ _, err := exec.LookPath(name)
+ if err != nil {
+ return false
+ }
+ return true
+}
diff --git a/v2/internal/signal/signal.go b/v2/internal/signal/signal.go
new file mode 100644
index 000000000..b394991ca
--- /dev/null
+++ b/v2/internal/signal/signal.go
@@ -0,0 +1,68 @@
+package signal
+
+import (
+ "os"
+ gosignal "os/signal"
+ "syscall"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Manager manages signals such as CTRL-C
+type Manager struct {
+ // Service Bus
+ bus *servicebus.ServiceBus
+
+ // logger
+ logger logger.CustomLogger
+
+ // signalChannel
+ signalchannel chan os.Signal
+
+ // Quit channel
+ quitChannel <-chan *servicebus.Message
+}
+
+// NewManager creates a new signal manager
+func NewManager(bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
+
+ // Register quit channel
+ quitChannel, err := bus.Subscribe("quit")
+ if err != nil {
+ return nil, err
+ }
+
+ result := &Manager{
+ bus: bus,
+ logger: logger.CustomLogger("Event Manager"),
+ signalchannel: make(chan os.Signal, 2),
+ quitChannel: quitChannel,
+ }
+
+ return result, nil
+}
+
+// Start the Signal Manager
+func (m *Manager) Start() {
+
+ // Hook into interrupts
+ gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
+
+ // Spin off signal listener
+ go func() {
+ running := true
+ for running {
+ select {
+ case <-m.signalchannel:
+ println()
+ m.logger.Trace("Ctrl+C detected. Shutting down...")
+ m.bus.Publish("quit", "ctrl-c pressed")
+ case <-m.quitChannel:
+ running = false
+ break
+ }
+ }
+ m.logger.Trace("Shutdown")
+ }()
+}
diff --git a/v2/internal/subsystem/binding.go b/v2/internal/subsystem/binding.go
new file mode 100644
index 000000000..55c712b75
--- /dev/null
+++ b/v2/internal/subsystem/binding.go
@@ -0,0 +1,113 @@
+package subsystem
+
+import (
+ "github.com/wailsapp/wails/v2/internal/binding"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/runtime/goruntime"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Binding is the Binding subsystem. It manages all service bus messages
+// starting with "binding".
+type Binding struct {
+ quitChannel <-chan *servicebus.Message
+ bindingChannel <-chan *servicebus.Message
+ running bool
+
+ // Binding db
+ bindings *binding.Bindings
+
+ // logger
+ logger logger.CustomLogger
+
+ // runtime
+ runtime *goruntime.Runtime
+}
+
+// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
+func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *goruntime.Runtime) (*Binding, error) {
+
+ // Register quit channel
+ quitChannel, err := bus.Subscribe("quit")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to event messages
+ bindingChannel, err := bus.Subscribe("binding")
+ if err != nil {
+ return nil, err
+ }
+
+ result := &Binding{
+ quitChannel: quitChannel,
+ bindingChannel: bindingChannel,
+ logger: logger.CustomLogger("Binding Subsystem"),
+ bindings: bindings,
+ runtime: runtime,
+ }
+
+ // Call WailsInit methods once the frontend is loaded
+ // TODO: Double check that this is actually being emitted
+ // when we want it to be
+ runtime.Events.On("wails:loaded", func(...interface{}) {
+ result.logger.Trace("Calling WailsInit() methods")
+ result.CallWailsInit()
+ })
+
+ return result, nil
+}
+
+// Start the subsystem
+func (b *Binding) Start() error {
+
+ b.running = true
+
+ b.logger.Trace("Starting")
+
+ // Spin off a go routine
+ go func() {
+ for b.running {
+ select {
+ case <-b.quitChannel:
+ b.running = false
+ case bindingMessage := <-b.bindingChannel:
+ b.logger.Trace("Got binding message: %+v", bindingMessage)
+ }
+ }
+
+ // Call shutdown
+ b.shutdown()
+ }()
+
+ return nil
+}
+
+// CallWailsInit will callback to the registered WailsInit
+// methods with the runtime object
+func (b *Binding) CallWailsInit() error {
+ for _, wailsinit := range b.bindings.DB().WailsInitMethods() {
+ _, err := wailsinit.Call([]interface{}{b.runtime})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// CallWailsShutdown will callback to the registered WailsShutdown
+// methods with the runtime object
+func (b *Binding) CallWailsShutdown() error {
+ for _, wailsshutdown := range b.bindings.DB().WailsShutdownMethods() {
+ _, err := wailsshutdown.Call([]interface{}{})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (b *Binding) shutdown() {
+ b.CallWailsShutdown()
+ b.logger.Trace("Shutdown")
+}
diff --git a/v2/internal/subsystem/call.go b/v2/internal/subsystem/call.go
new file mode 100644
index 000000000..1ade33ddd
--- /dev/null
+++ b/v2/internal/subsystem/call.go
@@ -0,0 +1,148 @@
+package subsystem
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/wailsapp/wails/v2/internal/binding"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Call is the Call subsystem. It manages all service bus messages
+// starting with "call".
+type Call struct {
+ quitChannel <-chan *servicebus.Message
+ callChannel <-chan *servicebus.Message
+ running bool
+
+ // bindings DB
+ DB *binding.DB
+
+ // ServiceBus
+ bus *servicebus.ServiceBus
+
+ // logger
+ logger logger.CustomLogger
+}
+
+// NewCall creates a new log subsystem
+func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB) (*Call, error) {
+
+ // Register quit channel
+ quitChannel, err := bus.Subscribe("quit")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to event messages
+ callChannel, err := bus.Subscribe("call:invoke")
+ if err != nil {
+ return nil, err
+ }
+
+ result := &Call{
+ quitChannel: quitChannel,
+ callChannel: callChannel,
+ logger: logger.CustomLogger("Call Subsystem"),
+ DB: DB,
+ bus: bus,
+ }
+
+ return result, nil
+}
+
+// Start the subsystem
+func (c *Call) Start() error {
+
+ c.running = true
+
+ // Spin off a go routine
+ go func() {
+ for c.running {
+ select {
+ case <-c.quitChannel:
+ c.running = false
+ case callMessage := <-c.callChannel:
+
+ c.processCall(callMessage)
+ }
+ }
+
+ // Call shutdown
+ c.shutdown()
+ }()
+
+ return nil
+}
+
+func (c *Call) processCall(callMessage *servicebus.Message) {
+
+ c.logger.Trace("Got message: %+v", callMessage)
+
+ // Extract payload
+ payload := callMessage.Data().(*message.CallMessage)
+
+ // Lookup method
+ registeredMethod := c.DB.GetMethod(payload.Name)
+
+ // Check we have it
+ if registeredMethod == nil {
+ c.sendError(fmt.Errorf("Method not registered"), payload, callMessage.Target())
+ return
+ }
+ c.logger.Trace("Got registered method: %+v", registeredMethod)
+
+ result, err := registeredMethod.Call(payload.Args)
+ if err != nil {
+ c.sendError(err, payload, callMessage.Target())
+ return
+ }
+ c.logger.Trace("registeredMethod.Call: %+v, %+v", result, err)
+ // process result
+ c.sendResult(result, payload, callMessage.Target())
+
+}
+
+func (c *Call) sendResult(result interface{}, payload *message.CallMessage, clientID string) {
+ c.logger.Trace("Sending success result with CallbackID '%s' : %+v\n", payload.CallbackID, result)
+ message := &CallbackMessage{
+ Result: result,
+ CallbackID: payload.CallbackID,
+ }
+ messageData, err := json.Marshal(message)
+ c.logger.Trace("json message data: %+v\n", string(messageData))
+ if err != nil {
+ // what now?
+ c.logger.Fatal(err.Error())
+ }
+ c.bus.PublishForTarget("call:result", string(messageData), clientID)
+}
+
+func (c *Call) sendError(err error, payload *message.CallMessage, clientID string) {
+ c.logger.Trace("Sending error result with CallbackID '%s' : %+v\n", payload.CallbackID, err.Error())
+ message := &CallbackMessage{
+ Err: err.Error(),
+ CallbackID: payload.CallbackID,
+ }
+
+ messageData, err := json.Marshal(message)
+ c.logger.Trace("json message data: %+v\n", string(messageData))
+ if err != nil {
+ // what now?
+ c.logger.Fatal(err.Error())
+ }
+ c.bus.PublishForTarget("call:result", string(messageData), clientID)
+}
+
+func (c *Call) shutdown() {
+ c.logger.Trace("Shutdown")
+}
+
+// CallbackMessage defines a message that contains the result of a call
+type CallbackMessage struct {
+ Result interface{} `json:"result"`
+ Err string `json:"error"`
+ CallbackID string `json:"callbackid"`
+}
diff --git a/v2/internal/subsystem/event.go b/v2/internal/subsystem/event.go
new file mode 100644
index 000000000..19c6c422e
--- /dev/null
+++ b/v2/internal/subsystem/event.go
@@ -0,0 +1,193 @@
+package subsystem
+
+import (
+ "strings"
+ "sync"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// eventListener holds a callback function which is invoked when
+// the event listened for is emitted. It has a counter which indicates
+// how the total number of events it is interested in. A value of zero
+// means it does not expire (default).
+type eventListener struct {
+ callback func(...interface{}) // Function to call with emitted event data
+ counter int64 // The number of times this callback may be called. -1 = infinite
+ delete bool // Flag to indicate that this listener should be deleted
+}
+
+// Event is the Eventing subsystem. It manages all service bus messages
+// starting with "event".
+type Event struct {
+ quitChannel <-chan *servicebus.Message
+ eventChannel <-chan *servicebus.Message
+ running bool
+
+ // Event listeners
+ listeners map[string][]*eventListener
+ notifyLock sync.RWMutex
+
+ // logger
+ logger logger.CustomLogger
+}
+
+// NewEvent creates a new log subsystem
+func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
+
+ // Register quit channel
+ quitChannel, err := bus.Subscribe("quit")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to event messages
+ eventChannel, err := bus.Subscribe("event")
+ if err != nil {
+ return nil, err
+ }
+
+ result := &Event{
+ quitChannel: quitChannel,
+ eventChannel: eventChannel,
+ logger: logger.CustomLogger("Event Subsystem"),
+ listeners: make(map[string][]*eventListener),
+ }
+
+ return result, nil
+}
+
+// RegisterListener provides a means of subscribing to events of type "eventName"
+func (e *Event) RegisterListener(eventName string, callback func(...interface{})) {
+
+ // Create new eventListener
+ thisListener := &eventListener{
+ callback: callback,
+ counter: 0,
+ delete: false,
+ }
+
+ e.notifyLock.Lock()
+ // Append the new listener to the listeners slice
+ e.listeners[eventName] = append(e.listeners[eventName], thisListener)
+ e.notifyLock.Unlock()
+}
+
+// Start the subsystem
+func (e *Event) Start() error {
+
+ e.logger.Trace("Starting")
+
+ e.running = true
+
+ // Spin off a go routine
+ go func() {
+ for e.running {
+ select {
+ case <-e.quitChannel:
+ e.running = false
+ break
+ case eventMessage := <-e.eventChannel:
+ splitTopic := strings.Split(eventMessage.Topic(), ":")
+ eventType := splitTopic[1]
+ switch eventType {
+ case "emit":
+ if len(splitTopic) != 4 {
+ e.logger.Error("Received emit message with invalid topic format. Expected 4 sections in topic, got %s", splitTopic)
+ continue
+ }
+ eventSource := splitTopic[3]
+ e.logger.Trace("Got Event Message: %s %+v", eventMessage.Topic(), eventMessage.Data())
+ event := eventMessage.Data().(*message.EventMessage)
+ eventName := event.Name
+ switch eventSource {
+
+ case "j":
+ // Notify Go Subscribers
+ e.logger.Trace("Notify Go subscribers to event '%s'", eventName)
+ go e.notifyListeners(eventName, event)
+ case "g":
+ // Notify Go listeners
+ e.logger.Trace("Got Go Event: %s", eventName)
+ go e.notifyListeners(eventName, event)
+ default:
+ e.logger.Error("unknown emit event message: %+v", eventMessage)
+ }
+ case "on":
+ // We wish to subscribe to an event channel
+ var message *message.OnEventMessage = eventMessage.Data().(*message.OnEventMessage)
+ eventName := message.Name
+ callback := message.Callback
+ e.RegisterListener(eventName, callback)
+ e.logger.Trace("Registered listener for event '%s' with callback %p", eventName, callback)
+ default:
+ e.logger.Error("unknown event message: %+v", eventMessage)
+ }
+ }
+ }
+
+ // Call shutdown
+ e.shutdown()
+ }()
+
+ return nil
+}
+
+// Notifies listeners for the given event name
+func (e *Event) notifyListeners(eventName string, message *message.EventMessage) {
+
+ // Get list of event listeners
+ listeners := e.listeners[eventName]
+ if listeners == nil {
+ println("no listeners for", eventName)
+ return
+ }
+
+ // Lock the listeners
+ e.notifyLock.Lock()
+
+ // We have a dirty flag to indicate that there are items to delete
+ itemsToDelete := false
+
+ // Callback in goroutine
+ for _, listener := range listeners {
+ if listener.counter > 0 {
+ listener.counter--
+ }
+
+ go listener.callback(message.Data...)
+
+ if listener.counter == 0 {
+ listener.delete = true
+ itemsToDelete = true
+ }
+ }
+
+ // Do we have items to delete?
+ if itemsToDelete == true {
+
+ // Create a new Listeners slice
+ var newListeners = []*eventListener{}
+
+ // Iterate over current listeners
+ for _, listener := range listeners {
+ // If we aren't deleting the listener, add it to the new list
+ if !listener.delete {
+ newListeners = append(newListeners, listener)
+ }
+ }
+
+ // Save new listeners
+ e.listeners[eventName] = newListeners
+ }
+
+ // Unlock
+ e.notifyLock.Unlock()
+
+}
+
+func (e *Event) shutdown() {
+ e.logger.Trace("Shutdown")
+}
diff --git a/v2/internal/subsystem/event_test.go b/v2/internal/subsystem/event_test.go
new file mode 100644
index 000000000..99219948c
--- /dev/null
+++ b/v2/internal/subsystem/event_test.go
@@ -0,0 +1,50 @@
+package subsystem
+
+import (
+ "os"
+ "sync"
+ "testing"
+
+ "github.com/matryer/is"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+func TestSingleTopic(t *testing.T) {
+
+ is := is.New(t)
+
+ var expected string = "I am a message!"
+ var actual string
+
+ var wg sync.WaitGroup
+
+ // Create new bus
+ myLogger := logger.New(os.Stdout)
+ myLogger.SetLogLevel(logger.TRACE)
+ bus := servicebus.New(myLogger)
+ eventSubsystem, _ := NewEvent(bus, myLogger)
+ eventSubsystem.Start()
+
+ eventSubsystem.RegisterListener("test", func(data ...interface{}) {
+ is.Equal(len(data), 1)
+ actual = data[0].(string)
+ wg.Done()
+ })
+
+ wg.Add(1)
+
+ eventMessage := &message.EventMessage{
+ Name: "test",
+ Data: []interface{}{"I am a message!"},
+ }
+
+ bus.Start()
+ bus.Publish("event:test:from:j", eventMessage)
+ wg.Wait()
+ bus.Stop()
+
+ is.Equal(actual, expected)
+
+}
diff --git a/v2/internal/subsystem/log.go b/v2/internal/subsystem/log.go
new file mode 100644
index 000000000..e57368d0e
--- /dev/null
+++ b/v2/internal/subsystem/log.go
@@ -0,0 +1,79 @@
+package subsystem
+
+import (
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Log is the Logging subsystem. It handles messages with topics starting
+// with "log:"
+type Log struct {
+ logChannel <-chan *servicebus.Message
+ quitChannel <-chan *servicebus.Message
+ running bool
+
+ // Logger!
+ logger *logger.Logger
+}
+
+// NewLog creates a new log subsystem
+func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger) (*Log, error) {
+
+ // Subscribe to log messages
+ logChannel, err := bus.Subscribe("log")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to quit messages
+ quitChannel, err := bus.Subscribe("quit")
+ if err != nil {
+ return nil, err
+ }
+
+ result := &Log{
+ logChannel: logChannel,
+ quitChannel: quitChannel,
+ logger: logger,
+ }
+
+ return result, nil
+}
+
+// Start the subsystem
+func (l *Log) Start() error {
+
+ l.running = true
+
+ // Spin off a go routine
+ go func() {
+ for l.running {
+ select {
+ case <-l.quitChannel:
+ l.running = false
+ break
+ case logMessage := <-l.logChannel:
+ logType := strings.TrimPrefix(logMessage.Topic(), "log:")
+ switch logType {
+ case "debug":
+ l.logger.Debug(logMessage.Data().(string))
+ case "info":
+ l.logger.Info(logMessage.Data().(string))
+ case "warning":
+ l.logger.Warning(logMessage.Data().(string))
+ case "error":
+ l.logger.Error(logMessage.Data().(string))
+ case "fatal":
+ l.logger.Fatal(logMessage.Data().(string))
+ default:
+ l.logger.Error("unknown log message: %+v", logMessage)
+ }
+ }
+ }
+ l.logger.Trace("Logger Shutdown")
+ }()
+
+ return nil
+}
diff --git a/v2/internal/subsystem/runtime.go b/v2/internal/subsystem/runtime.go
new file mode 100644
index 000000000..f692738cf
--- /dev/null
+++ b/v2/internal/subsystem/runtime.go
@@ -0,0 +1,116 @@
+package subsystem
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/runtime/goruntime"
+ "github.com/wailsapp/wails/v2/internal/servicebus"
+)
+
+// Runtime is the Runtime subsystem. It handles messages with topics starting
+// with "runtime:"
+type Runtime struct {
+ quitChannel <-chan *servicebus.Message
+ runtimeChannel <-chan *servicebus.Message
+ running bool
+
+ logger logger.CustomLogger
+
+ // Runtime library
+ runtime *goruntime.Runtime
+}
+
+// NewRuntime creates a new runtime subsystem
+func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger) (*Runtime, error) {
+
+ // Register quit channel
+ quitChannel, err := bus.Subscribe("quit")
+ if err != nil {
+ return nil, err
+ }
+
+ // Subscribe to log messages
+ runtimeChannel, err := bus.Subscribe("runtime")
+ if err != nil {
+ return nil, err
+ }
+
+ result := &Runtime{
+ quitChannel: quitChannel,
+ runtimeChannel: runtimeChannel,
+ logger: logger.CustomLogger("Runtime Subsystem"),
+ runtime: goruntime.New(bus),
+ }
+
+ return result, nil
+}
+
+// Start the subsystem
+func (r *Runtime) Start() error {
+
+ r.running = true
+
+ // Spin off a go routine
+ go func() {
+ for r.running {
+ select {
+ case <-r.quitChannel:
+ r.running = false
+ break
+ case runtimeMessage := <-r.runtimeChannel:
+ r.logger.Trace(fmt.Sprintf("Received message: %+v", runtimeMessage))
+ // Topics have the format: "runtime:category:call"
+ messageSlice := strings.Split(runtimeMessage.Topic(), ":")
+ if len(messageSlice) != 3 {
+ r.logger.Error("Invalid runtime message: %#v\n", runtimeMessage)
+ continue
+ }
+
+ category := messageSlice[1]
+ method := messageSlice[2]
+ var err error
+ switch category {
+ case "browser":
+ err = r.processBrowserMessage(method, runtimeMessage.Data())
+ default:
+ fmt.Errorf("unknown log message: %+v", runtimeMessage)
+ }
+
+ // If we had an error, log it
+ if err != nil {
+ r.logger.Error(err.Error())
+ }
+ }
+ }
+
+ // Call shutdown
+ r.shutdown()
+ }()
+
+ return nil
+}
+
+// GoRuntime returns the Go Runtime object
+func (r *Runtime) GoRuntime() *goruntime.Runtime {
+ return r.runtime
+}
+
+func (r *Runtime) shutdown() {
+ r.logger.Trace("Shutdown")
+}
+
+func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
+ switch method {
+ case "openurl":
+ url, ok := data.(string)
+ if !ok {
+ return fmt.Errorf("expected 1 string parameter for runtime:browser:openurl")
+ }
+ go r.runtime.Browser.Open(url)
+ default:
+ return fmt.Errorf("unknown method runtime:browser:%s", method)
+ }
+ return nil
+}
diff --git a/v2/internal/system/apps/apps.go b/v2/internal/system/apps/apps.go
new file mode 100644
index 000000000..fce6f9baf
--- /dev/null
+++ b/v2/internal/system/apps/apps.go
@@ -0,0 +1,5 @@
+package apps
+
+func Find() []Apps {
+
+}
diff --git a/v2/internal/system/operatingsystem/os.go b/v2/internal/system/operatingsystem/os.go
new file mode 100644
index 000000000..39f1de8e0
--- /dev/null
+++ b/v2/internal/system/operatingsystem/os.go
@@ -0,0 +1,13 @@
+package operatingsystem
+
+// OS contains information about the operating system
+type OS struct {
+ ID string
+ Name string
+ Version string
+}
+
+// Info retrieves information about the current platform
+func Info() (*OS, error) {
+ return platformInfo()
+}
diff --git a/v2/internal/system/operatingsystem/os_linux.go b/v2/internal/system/operatingsystem/os_linux.go
new file mode 100644
index 000000000..090bba2ac
--- /dev/null
+++ b/v2/internal/system/operatingsystem/os_linux.go
@@ -0,0 +1,51 @@
+// +build linux
+
+package operatingsystem
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+)
+
+// platformInfo is the platform specific method to get system information
+func platformInfo() (*OS, error) {
+ _, err := os.Stat("/etc/os-release")
+ if os.IsNotExist(err) {
+ return nil, fmt.Errorf("unable to read system information")
+ }
+
+ osRelease, _ := ioutil.ReadFile("/etc/os-release")
+ return parseOsRelease(string(osRelease)), nil
+}
+
+func parseOsRelease(osRelease string) *OS {
+
+ // Default value
+ var result OS
+ result.ID = "Unknown"
+ result.Name = "Unknown"
+ result.Version = "Unknown"
+
+ // Split into lines
+ lines := strings.Split(osRelease, "\n")
+ // Iterate lines
+ for _, line := range lines {
+ // Split each line by the equals char
+ splitLine := strings.SplitN(line, "=", 2)
+ // Check we have
+ if len(splitLine) != 2 {
+ continue
+ }
+ switch splitLine[0] {
+ case "ID":
+ result.ID = strings.ToLower(strings.Trim(splitLine[1], "\""))
+ case "NAME":
+ result.Name = strings.Trim(splitLine[1], "\"")
+ case "VERSION_ID":
+ result.Version = strings.Trim(splitLine[1], "\"")
+ }
+ }
+ return &result
+}
diff --git a/v2/internal/system/packagemanager/apt.go b/v2/internal/system/packagemanager/apt.go
new file mode 100644
index 000000000..cc37e55bb
--- /dev/null
+++ b/v2/internal/system/packagemanager/apt.go
@@ -0,0 +1,97 @@
+// +build linux
+
+package packagemanager
+
+import (
+ "regexp"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// Apt represents the Apt manager
+type Apt struct {
+ name string
+ osid string
+}
+
+// NewApt creates a new Apt instance
+func NewApt(osid string) *Apt {
+ return &Apt{
+ name: "apt",
+ osid: osid,
+ }
+}
+
+// Packages returns the libraries that we need for Wails to compile
+// They will potentially differ on different distributions or versions
+func (a *Apt) Packages() packagemap {
+ return packagemap{
+ "libgtk-3": []*Package{
+ {Name: "libgtk-3-dev", SystemPackage: true, Library: true},
+ },
+ "libwebkit": []*Package{
+ {Name: "libwebkit2gtk-4.0-dev", SystemPackage: true, Library: true},
+ },
+ "gcc": []*Package{
+ {Name: "build-essential", SystemPackage: true},
+ },
+ "pkg-config": []*Package{
+ {Name: "pkg-config", SystemPackage: true},
+ },
+ "npm": []*Package{
+ {Name: "npm", SystemPackage: true},
+ },
+ "docker": []*Package{
+ {Name: "docker.io", SystemPackage: true, Optional: true},
+ },
+ }
+}
+
+// Name returns the name of the package manager
+func (a *Apt) Name() string {
+ return a.name
+}
+
+// PackageInstalled tests if the given package name is installed
+func (a *Apt) PackageInstalled(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "apt", "-qq", "list", pkg.Name)
+ return strings.Contains(stdout, "[installed]"), err
+}
+
+// PackageAvailable tests if the given package is available for installation
+func (a *Apt) PackageAvailable(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "apt", "-qq", "list", pkg.Name)
+ // We add a space to ensure we get a full match, not partial match
+ output := a.removeEscapeSequences(stdout)
+ installed := strings.HasPrefix(output, pkg.Name)
+ a.getPackageVersion(pkg, output)
+ return installed, err
+}
+
+// InstallCommand returns the package manager specific command to install a package
+func (a *Apt) InstallCommand(pkg *Package) string {
+ if pkg.SystemPackage == false {
+ return pkg.InstallCommand[a.osid]
+ }
+ return "sudo apt install " + pkg.Name
+}
+
+func (a *Apt) removeEscapeSequences(in string) string {
+ escapechars, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`)
+ return escapechars.ReplaceAllString(in, "")
+}
+
+func (a *Apt) getPackageVersion(pkg *Package, output string) {
+
+ splitOutput := strings.Split(output, " ")
+ if len(splitOutput) > 1 {
+ pkg.Version = splitOutput[1]
+ }
+}
diff --git a/v2/internal/system/packagemanager/emerge.go b/v2/internal/system/packagemanager/emerge.go
new file mode 100644
index 000000000..0caa3117c
--- /dev/null
+++ b/v2/internal/system/packagemanager/emerge.go
@@ -0,0 +1,117 @@
+// +build linux
+
+package packagemanager
+
+import (
+ "os/exec"
+ "regexp"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// Emerge represents the Emerge package manager
+type Emerge struct {
+ name string
+ osid string
+}
+
+// NewEmerge creates a new Emerge instance
+func NewEmerge(osid string) *Emerge {
+ return &Emerge{
+ name: "emerge",
+ osid: osid,
+ }
+}
+
+// Packages returns the libraries that we need for Wails to compile
+// They will potentially differ on different distributions or versions
+func (e *Emerge) Packages() packagemap {
+ return packagemap{
+ "libgtk-3": []*Package{
+ {Name: "x11-libs/gtk+", SystemPackage: true, Library: true},
+ },
+ "libwebkit": []*Package{
+ {Name: "net-libs/webkit-gtk", SystemPackage: true, Library: true},
+ },
+ "gcc": []*Package{
+ {Name: "sys-devel/gcc", SystemPackage: true},
+ },
+ "pkg-config": []*Package{
+ {Name: "dev-util/pkgconf", SystemPackage: true},
+ },
+ "npm": []*Package{
+ {Name: "net-libs/nodejs", SystemPackage: true},
+ },
+ "docker": []*Package{
+ {Name: "app-emulation/docker", SystemPackage: true, Optional: true},
+ },
+ }
+}
+
+// Name returns the name of the package manager
+func (e *Emerge) Name() string {
+ return e.name
+}
+
+// PackageInstalled tests if the given package name is installed
+func (e *Emerge) PackageInstalled(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "emerge", "-s", pkg.Name+"$")
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+
+ regex := `.*\*\s+` + regexp.QuoteMeta(pkg.Name) + `\n(?:\S|\s)+?Latest version installed: (.*)`
+ installedRegex := regexp.MustCompile(regex)
+ matches := installedRegex.FindStringSubmatch(stdout)
+ pkg.Version = ""
+ noOfMatches := len(matches)
+ installed := false
+ if noOfMatches > 1 && matches[1] != "[ Not Installed ]" {
+ installed = true
+ pkg.Version = strings.TrimSpace(matches[1])
+ }
+ return installed, err
+}
+
+// PackageAvailable tests if the given package is available for installation
+func (e *Emerge) PackageAvailable(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "emerge", "-s", pkg.Name+"$")
+ // We add a space to ensure we get a full match, not partial match
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+
+ installedRegex := regexp.MustCompile(`.*\*\s+` + regexp.QuoteMeta(pkg.Name) + `\n(?:\S|\s)+?Latest version available: (.*)`)
+ matches := installedRegex.FindStringSubmatch(stdout)
+ pkg.Version = ""
+ noOfMatches := len(matches)
+ available := false
+ if noOfMatches > 1 {
+ available = true
+ pkg.Version = strings.TrimSpace(matches[1])
+ }
+ return available, nil
+}
+
+// InstallCommand returns the package manager specific command to install a package
+func (e *Emerge) InstallCommand(pkg *Package) string {
+ if pkg.SystemPackage == false {
+ return pkg.InstallCommand[e.osid]
+ }
+ return "sudo emerge " + pkg.Name
+}
diff --git a/v2/internal/system/packagemanager/eopkg.go b/v2/internal/system/packagemanager/eopkg.go
new file mode 100644
index 000000000..6a7a8fd95
--- /dev/null
+++ b/v2/internal/system/packagemanager/eopkg.go
@@ -0,0 +1,114 @@
+// +build linux
+
+package packagemanager
+
+import (
+ "regexp"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// Eopkg represents the Eopkg manager
+type Eopkg struct {
+ name string
+ osid string
+}
+
+// NewEopkg creates a new Eopkg instance
+func NewEopkg(osid string) *Eopkg {
+ result := &Eopkg{
+ name: "eopkg",
+ osid: osid,
+ }
+ result.intialiseName()
+ return result
+}
+
+// Packages returns the packages that we need for Wails to compile
+// They will potentially differ on different distributions or versions
+func (e *Eopkg) Packages() packagemap {
+ return packagemap{
+ "libgtk-3": []*Package{
+ {Name: "libgtk-3-devel", SystemPackage: true, Library: true},
+ },
+ "libwebkit": []*Package{
+ {Name: "libwebkit-gtk-devel", SystemPackage: true, Library: true},
+ },
+ "gcc": []*Package{
+ {Name: "gcc", SystemPackage: true},
+ },
+ "pkg-config": []*Package{
+ {Name: "pkg-config", SystemPackage: true},
+ },
+ "npm": []*Package{
+ {Name: "nodejs", SystemPackage: true},
+ },
+ "docker": []*Package{
+ {Name: "docker", SystemPackage: true, Optional: true},
+ },
+ }
+}
+
+// Name returns the name of the package manager
+func (e *Eopkg) Name() string {
+ return e.name
+}
+
+// PackageInstalled tests if the given package is installed
+func (e *Eopkg) PackageInstalled(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "eopkg", "info", pkg.Name)
+ return strings.HasPrefix(stdout, "Installed"), err
+}
+
+// PackageAvailable tests if the given package is available for installation
+func (e *Eopkg) PackageAvailable(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "eopkg", "info", pkg.Name)
+ // We add a space to ensure we get a full match, not partial match
+ output := e.removeEscapeSequences(stdout)
+ installed := strings.Contains(output, "Package found in Solus repository")
+ e.getPackageVersion(pkg, output)
+ return installed, err
+}
+
+// InstallCommand returns the package manager specific command to install a package
+func (e *Eopkg) InstallCommand(pkg *Package) string {
+ if pkg.SystemPackage == false {
+ return pkg.InstallCommand[e.osid]
+ }
+ return "sudo eopkg it " + pkg.Name
+}
+
+func (e *Eopkg) removeEscapeSequences(in string) string {
+ escapechars, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`)
+ return escapechars.ReplaceAllString(in, "")
+}
+
+func (e *Eopkg) intialiseName() {
+ result := "eopkg"
+ stdout, _, err := shell.RunCommand(".", "eopkg", "--version")
+ if err == nil {
+ result = strings.TrimSpace(stdout)
+ }
+ e.name = result
+}
+
+func (e *Eopkg) getPackageVersion(pkg *Package, output string) {
+
+ versionRegex := regexp.MustCompile(`.*Name.*version:\s+(.*)+, release: (.*)`)
+ matches := versionRegex.FindStringSubmatch(output)
+ pkg.Version = ""
+ noOfMatches := len(matches)
+ if noOfMatches > 1 {
+ pkg.Version = matches[1]
+ if noOfMatches > 2 {
+ pkg.Version += " (r" + matches[2] + ")"
+ }
+ }
+}
diff --git a/v2/internal/system/packagemanager/packagemanager.go b/v2/internal/system/packagemanager/packagemanager.go
new file mode 100644
index 000000000..faa9bda86
--- /dev/null
+++ b/v2/internal/system/packagemanager/packagemanager.go
@@ -0,0 +1,231 @@
+// +build linux
+
+package packagemanager
+
+import (
+ "sort"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// PackageManager is a common interface across all package managers
+type PackageManager interface {
+ Name() string
+ Packages() packagemap
+ PackageInstalled(*Package) (bool, error)
+ PackageAvailable(*Package) (bool, error)
+ InstallCommand(*Package) string
+}
+
+// Package contains information about a system package
+type Package struct {
+ Name string
+ Version string
+ InstallCommand map[string]string
+ SystemPackage bool
+ Library bool
+ Optional bool
+}
+
+// A list of package manager commands
+var pmcommands = []string{
+ "eopkg",
+ "apt",
+ "yum",
+ "pacman",
+ "emerge",
+ "zypper",
+}
+
+type packagemap = map[string][]*Package
+
+// Find will attempt to find the system package manager
+func Find(osid string) PackageManager {
+
+ // Loop over pmcommands
+ for _, pmname := range pmcommands {
+ if shell.CommandExists(pmname) {
+ return newPackageManager(pmname, osid)
+ }
+ }
+ return nil
+}
+
+func newPackageManager(pmname string, osid string) PackageManager {
+ switch pmname {
+ case "eopkg":
+ return NewEopkg(osid)
+ case "apt":
+ return NewApt(osid)
+ case "yum":
+ return NewYum(osid)
+ case "pacman":
+ return NewPacman(osid)
+ case "emerge":
+ return NewEmerge(osid)
+ case "zypper":
+ return NewZypper(osid)
+ }
+ return nil
+}
+
+// Dependancy represents a system package that we require
+type Dependancy struct {
+ Name string
+ PackageName string
+ Installed bool
+ InstallCommand string
+ Version string
+ Optional bool
+ External bool
+}
+
+// DependencyList is a list of Dependency instances
+type DependencyList []*Dependancy
+
+// InstallAllRequiredCommand returns the command you need to use to install all required dependencies
+func (d DependencyList) InstallAllRequiredCommand() string {
+
+ result := ""
+ for _, dependency := range d {
+ if dependency.PackageName != "" {
+ if !dependency.Installed && !dependency.Optional {
+ if result == "" {
+ result = dependency.InstallCommand
+ } else {
+ result += " " + dependency.PackageName
+ }
+ }
+ }
+ }
+
+ return result
+}
+
+// InstallAllOptionalCommand returns the command you need to use to install all optional dependencies
+func (d DependencyList) InstallAllOptionalCommand() string {
+
+ result := ""
+ for _, dependency := range d {
+ if dependency.PackageName != "" {
+ if !dependency.Installed && dependency.Optional {
+ if result == "" {
+ result = dependency.InstallCommand
+ } else {
+ result += " " + dependency.PackageName
+ }
+ }
+ }
+ }
+
+ return result
+}
+
+// Dependancies scans the system for required dependancies
+// Returns a list of dependancies search for, whether they were found
+// and whether they were installed
+func Dependancies(p PackageManager) (DependencyList, error) {
+
+ var dependencies DependencyList
+
+ for name, packages := range p.Packages() {
+ dependency := &Dependancy{Name: name}
+ for _, pkg := range packages {
+ dependency.Optional = pkg.Optional
+ dependency.External = !pkg.SystemPackage
+ dependency.InstallCommand = p.InstallCommand(pkg)
+ packageavailable, err := p.PackageAvailable(pkg)
+ if err != nil {
+ return nil, err
+ }
+ if packageavailable {
+ dependency.Version = pkg.Version
+ dependency.PackageName = pkg.Name
+ installed, err := p.PackageInstalled(pkg)
+ if err != nil {
+ return nil, err
+ }
+ if installed {
+ dependency.Installed = true
+ dependency.Version = pkg.Version
+ if !pkg.Library {
+ dependency.Version = AppVersion(name)
+ }
+ } else {
+ dependency.InstallCommand = p.InstallCommand(pkg)
+ }
+ break
+ }
+ }
+ dependencies = append(dependencies, dependency)
+ }
+
+ // Sort dependencies
+ sort.Slice(dependencies, func(i, j int) bool {
+ return dependencies[i].Name < dependencies[j].Name
+ })
+
+ return dependencies, nil
+}
+
+// AppVersion returns the version for application related to the given package
+func AppVersion(name string) string {
+
+ if name == "gcc" {
+ return gccVersion()
+ }
+
+ if name == "pkg-config" {
+ return pkgConfigVersion()
+ }
+
+ if name == "npm" {
+ return npmVersion()
+ }
+
+ if name == "docker" {
+ return dockerVersion()
+ }
+
+ return ""
+
+}
+
+func gccVersion() string {
+
+ var version string
+ var err error
+
+ // Try "-dumpfullversion"
+ version, _, err = shell.RunCommand(".", "gcc", "-dumpfullversion")
+ if err != nil {
+
+ // Try -dumpversion
+ // We ignore the error as this function is not for testing whether the
+ // application exists, only that we can get the version number
+ dumpversion, _, err := shell.RunCommand(".", "gcc", "-dumpversion")
+ if err == nil {
+ version = dumpversion
+ }
+ }
+ return strings.TrimSpace(version)
+}
+
+func pkgConfigVersion() string {
+ version, _, _ := shell.RunCommand(".", "pkg-config", "--version")
+ return strings.TrimSpace(version)
+}
+
+func npmVersion() string {
+ version, _, _ := shell.RunCommand(".", "npm", "--version")
+ return strings.TrimSpace(version)
+}
+
+func dockerVersion() string {
+ version, _, _ := shell.RunCommand(".", "docker", "--version")
+ version = strings.TrimPrefix(version, "Docker version ")
+ version = strings.ReplaceAll(version, ", build ", " (")
+ version = strings.TrimSpace(version) + ")"
+ return version
+}
diff --git a/v2/internal/system/packagemanager/pacman.go b/v2/internal/system/packagemanager/pacman.go
new file mode 100644
index 000000000..6378c324d
--- /dev/null
+++ b/v2/internal/system/packagemanager/pacman.go
@@ -0,0 +1,114 @@
+// +build linux
+
+package packagemanager
+
+import (
+ "os/exec"
+ "regexp"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// Pacman represents the Pacman package manager
+type Pacman struct {
+ name string
+ osid string
+}
+
+// NewPacman creates a new Pacman instance
+func NewPacman(osid string) *Pacman {
+ return &Pacman{
+ name: "pacman",
+ osid: osid,
+ }
+}
+
+// Packages returns the libraries that we need for Wails to compile
+// They will potentially differ on different distributions or versions
+func (p *Pacman) Packages() packagemap {
+ return packagemap{
+ "libgtk-3": []*Package{
+ {Name: "gtk3", SystemPackage: true, Library: true},
+ },
+ "libwebkit": []*Package{
+ {Name: "webkit2gtk", SystemPackage: true, Library: true},
+ },
+ "gcc": []*Package{
+ {Name: "gcc", SystemPackage: true},
+ },
+ "pkg-config": []*Package{
+ {Name: "pkgconf", SystemPackage: true},
+ },
+ "npm": []*Package{
+ {Name: "npm", SystemPackage: true},
+ },
+ "docker": []*Package{
+ {Name: "docker", SystemPackage: true, Optional: true},
+ },
+ }
+}
+
+// Name returns the name of the package manager
+func (p *Pacman) Name() string {
+ return p.name
+}
+
+// PackageInstalled tests if the given package name is installed
+func (p *Pacman) PackageInstalled(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "pacman", "-Q", pkg.Name)
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+
+ splitoutput := strings.Split(stdout, "\n")
+ for _, line := range splitoutput {
+ if strings.HasPrefix(line, pkg.Name) {
+ splitline := strings.Split(line, " ")
+ pkg.Version = strings.TrimSpace(splitline[1])
+ }
+ }
+
+ return true, err
+}
+
+// PackageAvailable tests if the given package is available for installation
+func (p *Pacman) PackageAvailable(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ output, _, err := shell.RunCommand(".", "pacman", "-Si", pkg.Name)
+ // We add a space to ensure we get a full match, not partial match
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+
+ reg := regexp.MustCompile(`.*Version.*?:\s+(.*)`)
+ matches := reg.FindStringSubmatch(output)
+ pkg.Version = ""
+ noOfMatches := len(matches)
+ if noOfMatches > 1 {
+ pkg.Version = strings.TrimSpace(matches[1])
+ }
+
+ return true, nil
+}
+
+// InstallCommand returns the package manager specific command to install a package
+func (p *Pacman) InstallCommand(pkg *Package) string {
+ if pkg.SystemPackage == false {
+ return pkg.InstallCommand[p.osid]
+ }
+ return "sudo pacman -S " + pkg.Name
+}
diff --git a/v2/internal/system/packagemanager/yum.go b/v2/internal/system/packagemanager/yum.go
new file mode 100644
index 000000000..de2d6d1ce
--- /dev/null
+++ b/v2/internal/system/packagemanager/yum.go
@@ -0,0 +1,126 @@
+// +build linux
+
+package packagemanager
+
+import (
+ "os/exec"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// Yum represents the Yum manager
+type Yum struct {
+ name string
+ osid string
+}
+
+// NewYum creates a new Yum instance
+func NewYum(osid string) *Yum {
+ return &Yum{
+ name: "yum",
+ osid: osid,
+ }
+}
+
+// Packages returns the libraries that we need for Wails to compile
+// They will potentially differ on different distributions or versions
+func (y *Yum) Packages() packagemap {
+ return packagemap{
+ "libgtk-3": []*Package{
+ {Name: "gtk3-devel", SystemPackage: true, Library: true},
+ },
+ "libwebkit": []*Package{
+ {Name: "webkit2gtk3-devel", SystemPackage: true, Library: true},
+ // {Name: "webkitgtk3-devel", SystemPackage: true, Library: true},
+ },
+ "gcc": []*Package{
+ {Name: "gcc-c++", SystemPackage: true},
+ },
+ "pkg-config": []*Package{
+ {Name: "pkgconf-pkg-config", SystemPackage: true},
+ },
+ "npm": []*Package{
+ {Name: "npm", SystemPackage: true},
+ },
+ "docker": []*Package{
+ {
+ SystemPackage: false,
+ Optional: true,
+ InstallCommand: map[string]string{
+ "centos": "Follow the guide: https://docs.docker.com/engine/install/centos/",
+ "fedora": "Follow the guide: https://docs.docker.com/engine/install/fedora/",
+ },
+ },
+ },
+ }
+}
+
+// Name returns the name of the package manager
+func (y *Yum) Name() string {
+ return y.name
+}
+
+// PackageInstalled tests if the given package name is installed
+func (y *Yum) PackageInstalled(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "yum", "info", "installed", pkg.Name)
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+
+ splitoutput := strings.Split(stdout, "\n")
+ for _, line := range splitoutput {
+ if strings.HasPrefix(line, "Version") {
+ splitline := strings.Split(line, ":")
+ pkg.Version = strings.TrimSpace(splitline[1])
+ }
+ }
+
+ return true, err
+}
+
+// PackageAvailable tests if the given package is available for installation
+func (y *Yum) PackageAvailable(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "yum", "info", pkg.Name)
+ // We add a space to ensure we get a full match, not partial match
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+ splitoutput := strings.Split(stdout, "\n")
+ for _, line := range splitoutput {
+ if strings.HasPrefix(line, "Version") {
+ splitline := strings.Split(line, ":")
+ pkg.Version = strings.TrimSpace(splitline[1])
+ }
+ }
+ return true, nil
+}
+
+// InstallCommand returns the package manager specific command to install a package
+func (y *Yum) InstallCommand(pkg *Package) string {
+ if pkg.SystemPackage == false {
+ return pkg.InstallCommand[y.osid]
+ }
+ return "sudo yum install " + pkg.Name
+}
+
+func (y *Yum) getPackageVersion(pkg *Package, output string) {
+ splitOutput := strings.Split(output, " ")
+ if len(splitOutput) > 0 {
+ pkg.Version = splitOutput[1]
+ }
+}
diff --git a/v2/internal/system/packagemanager/zypper.go b/v2/internal/system/packagemanager/zypper.go
new file mode 100644
index 000000000..af9d2e3d1
--- /dev/null
+++ b/v2/internal/system/packagemanager/zypper.go
@@ -0,0 +1,120 @@
+// +build linux
+
+package packagemanager
+
+import (
+ "os/exec"
+ "regexp"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// Zypper represents the Zypper package manager
+type Zypper struct {
+ name string
+ osid string
+}
+
+// NewZypper creates a new Zypper instance
+func NewZypper(osid string) *Zypper {
+ return &Zypper{
+ name: "zypper",
+ osid: osid,
+ }
+}
+
+// Packages returns the libraries that we need for Wails to compile
+// They will potentially differ on different distributions or versions
+func (z *Zypper) Packages() packagemap {
+ return packagemap{
+ "libgtk-3": []*Package{
+ {Name: "gtk3-devel", SystemPackage: true, Library: true},
+ },
+ "libwebkit": []*Package{
+ {Name: "webkit2gtk3-devel", SystemPackage: true, Library: true},
+ },
+ "gcc": []*Package{
+ {Name: "gcc-c++", SystemPackage: true},
+ },
+ "pkg-config": []*Package{
+ {Name: "pkg-config", SystemPackage: true},
+ },
+ "npm": []*Package{
+ {Name: "npm10", SystemPackage: true},
+ },
+ "docker": []*Package{
+ {Name: "docker", SystemPackage: true, Optional: true},
+ },
+ }
+}
+
+// Name returns the name of the package manager
+func (z *Zypper) Name() string {
+ return z.name
+}
+
+// PackageInstalled tests if the given package name is installed
+func (z *Zypper) PackageInstalled(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "zypper", "info", pkg.Name)
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+ reg := regexp.MustCompile(`.*Installed\s*:\s*(Yes)\s*`)
+ matches := reg.FindStringSubmatch(stdout)
+ pkg.Version = ""
+ noOfMatches := len(matches)
+ if noOfMatches > 1 {
+ z.getPackageVersion(pkg, stdout)
+ }
+ return noOfMatches > 1, err
+}
+
+// PackageAvailable tests if the given package is available for installation
+func (z *Zypper) PackageAvailable(pkg *Package) (bool, error) {
+ if pkg.SystemPackage == false {
+ return false, nil
+ }
+ stdout, _, err := shell.RunCommand(".", "zypper", "info", pkg.Name)
+ // We add a space to ensure we get a full match, not partial match
+ if err != nil {
+ _, ok := err.(*exec.ExitError)
+ if ok {
+ return false, nil
+ }
+ return false, err
+ }
+
+ available := strings.Contains(stdout, "Information for package")
+ if available {
+ z.getPackageVersion(pkg, stdout)
+ }
+
+ return available, nil
+}
+
+// InstallCommand returns the package manager specific command to install a package
+func (z *Zypper) InstallCommand(pkg *Package) string {
+ if pkg.SystemPackage == false {
+ return pkg.InstallCommand[z.osid]
+ }
+ return "sudo zypper in " + pkg.Name
+}
+
+func (z *Zypper) getPackageVersion(pkg *Package, output string) {
+
+ reg := regexp.MustCompile(`.*Version.*:(.*)`)
+ matches := reg.FindStringSubmatch(output)
+ pkg.Version = ""
+ noOfMatches := len(matches)
+ if noOfMatches > 1 {
+ pkg.Version = strings.TrimSpace(matches[1])
+ }
+}
diff --git a/v2/internal/system/system.go b/v2/internal/system/system.go
new file mode 100644
index 000000000..dccf17c82
--- /dev/null
+++ b/v2/internal/system/system.go
@@ -0,0 +1,26 @@
+package system
+
+import (
+ "github.com/wailsapp/wails/v2/internal/system/operatingsystem"
+ "github.com/wailsapp/wails/v2/internal/system/packagemanager"
+)
+
+// Info holds information about the current operating system,
+// package manager and required dependancies
+type Info struct {
+ OS *operatingsystem.OS
+ PM packagemanager.PackageManager
+ Dependencies packagemanager.DependencyList
+}
+
+// GetInfo scans the system for operating system details,
+// the system package manager and the status of required
+// dependancies.
+func GetInfo() (*Info, error) {
+ var result Info
+ err := result.discover()
+ if err != nil {
+ return nil, err
+ }
+ return &result, nil
+}
diff --git a/v2/internal/system/system_linux.go b/v2/internal/system/system_linux.go
new file mode 100644
index 000000000..4cb1d9599
--- /dev/null
+++ b/v2/internal/system/system_linux.go
@@ -0,0 +1,29 @@
+// +build linux
+
+package system
+
+import (
+ "github.com/wailsapp/wails/v2/internal/system/operatingsystem"
+ "github.com/wailsapp/wails/v2/internal/system/packagemanager"
+)
+
+func (i *Info) discover() error {
+
+ var err error
+ osinfo, err := operatingsystem.Info()
+ if err != nil {
+ return err
+ }
+ i.OS = osinfo
+
+ i.PM = packagemanager.Find(osinfo.ID)
+ if i.PM != nil {
+ dependencies, err := packagemanager.Dependancies(i.PM)
+ if err != nil {
+ return err
+ }
+ i.Dependencies = dependencies
+ }
+
+ return nil
+}
diff --git a/v2/internal/system/system_windows.go b/v2/internal/system/system_windows.go
new file mode 100644
index 000000000..1f1478469
--- /dev/null
+++ b/v2/internal/system/system_windows.go
@@ -0,0 +1,15 @@
+// +build windows
+
+package system
+
+import (
+ "fmt"
+ "syscall"
+)
+
+func (i *Info) discover() {
+ dll := syscall.MustLoadDLL("kernel32.dll")
+ p := dll.MustFindProc("GetVersion")
+ v, _, _ := p.Call()
+ fmt.Printf("Windows version %d.%d (Build %d)\n", byte(v), uint8(v>>8), uint16(v>>16))
+}
diff --git a/v2/internal/templates/templates.go b/v2/internal/templates/templates.go
new file mode 100644
index 000000000..a52401df0
--- /dev/null
+++ b/v2/internal/templates/templates.go
@@ -0,0 +1,247 @@
+package templates
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/leaanthony/gosod"
+ "github.com/leaanthony/slicer"
+ "github.com/olekukonko/tablewriter"
+ "github.com/wailsapp/wails/v2/internal/fs"
+ "github.com/wailsapp/wails/v2/internal/logger"
+)
+
+// Cahce for the templates
+// We use this because we need different views of the same data
+var templateCache []Template = nil
+
+// Data contains the data we wish to embed during template installation
+type Data struct {
+ ProjectName string
+ BinaryName string
+ WailsVersion string
+ NPMProjectName string
+ Author string
+ WailsDirectory string
+}
+
+// Options for installing a template
+type Options struct {
+ ProjectName string
+ TemplateName string
+ BinaryName string
+ TargetDir string
+ Logger *logger.Logger
+}
+
+// Template holds data relating to a template
+// including the metadata stored in template.json
+type Template struct {
+
+ // Template details
+ Name string `json:"name"`
+ ShortName string `json:"shortname"`
+ Author string `json:"author"`
+ Description string `json:"description"`
+ HelpURL string `json:"helpurl"`
+
+ // Other data
+ Directory string `json:"-"`
+}
+
+func parseTemplate(directory string) (Template, error) {
+ templateJSON := filepath.Join(directory, "template.json")
+ var result Template
+ data, err := ioutil.ReadFile(templateJSON)
+ if err != nil {
+ return result, err
+ }
+
+ result.Directory = directory
+ err = json.Unmarshal(data, &result)
+ return result, err
+}
+
+// TemplateShortNames returns a slicer of short template names
+func TemplateShortNames() (*slicer.StringSlicer, error) {
+
+ var result slicer.StringSlicer
+
+ // If the cache isn't loaded, load it
+ if templateCache == nil {
+ err := loadTemplateCache()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ for _, template := range templateCache {
+ result.Add(template.ShortName)
+ }
+
+ return &result, nil
+}
+
+// List returns the list of available templates
+func List() ([]Template, error) {
+
+ // If the cache isn't loaded, load it
+ if templateCache == nil {
+ err := loadTemplateCache()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return templateCache, nil
+}
+
+// getTemplateByShortname returns the template with the given short name
+func getTemplateByShortname(shortname string) (Template, error) {
+
+ var result Template
+
+ // If the cache isn't loaded, load it
+ if templateCache == nil {
+ err := loadTemplateCache()
+ if err != nil {
+ return result, err
+ }
+ }
+
+ for _, template := range templateCache {
+ if template.ShortName == shortname {
+ return template, nil
+ }
+ }
+
+ return result, fmt.Errorf("shortname '%s' is not a valid template shortname", shortname)
+}
+
+// Loads the template cache
+func loadTemplateCache() error {
+
+ // Get local template directory
+ templateDir := fs.RelativePath("templates")
+
+ // Get directories
+ files, err := ioutil.ReadDir(templateDir)
+ if err != nil {
+ return err
+ }
+
+ // Reset cache
+ templateCache = []Template{}
+
+ for _, file := range files {
+ if file.IsDir() {
+ templateDir := filepath.Join(templateDir, file.Name())
+ template, err := parseTemplate(templateDir)
+ if err != nil {
+ // Cannot parse this template, continue
+ continue
+ }
+ templateCache = append(templateCache, template)
+ }
+ }
+
+ return nil
+}
+
+// Install the given template
+func Install(options *Options) error {
+
+ // Get cwd
+ cwd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+
+ // Did the user want to install in current directory?
+ if options.TargetDir == "." {
+ // Yes - use cwd
+ options.TargetDir = cwd
+ } else {
+ // Get the absolute path of the given directory
+ targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir))
+ if err != nil {
+ return err
+ }
+ options.TargetDir = targetDir
+ if !fs.DirExists(options.TargetDir) {
+ err := fs.Mkdir(options.TargetDir)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ // Get template
+ template, err := getTemplateByShortname(options.TemplateName)
+ if err != nil {
+ return err
+ }
+
+ // Use Gosod to install the template
+ installer, err := gosod.TemplateDir(template.Directory)
+ if err != nil {
+ return err
+ }
+
+ // Ignore template.json files
+ installer.IgnoreFilename("template.json")
+
+ // Setup the data.
+ // We use the directory name for the binary name, like Go
+ BinaryName := filepath.Base(options.TargetDir)
+ NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", ""))
+ localWailsDirectory := fs.RelativePath("../..")
+ templateData := &Data{
+ ProjectName: options.ProjectName,
+ BinaryName: filepath.Base(options.TargetDir),
+ NPMProjectName: NPMProjectName,
+ WailsDirectory: localWailsDirectory,
+ }
+
+ // Extract the template
+ err = installer.Extract(options.TargetDir, templateData)
+ if err != nil {
+ return err
+ }
+
+ // Calculate the directory name
+ return nil
+}
+
+// OutputList prints the list of available tempaltes to the given logger
+func OutputList(logger *logger.Logger) error {
+ templates, err := List()
+ if err != nil {
+ return err
+ }
+
+ for _, writer := range logger.Writers() {
+ table := tablewriter.NewWriter(writer)
+ table.SetHeader([]string{"Template", "Short Name", "Description"})
+ table.SetAutoWrapText(false)
+ table.SetAutoFormatHeaders(true)
+ table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
+ table.SetAlignment(tablewriter.ALIGN_LEFT)
+ table.SetCenterSeparator("")
+ table.SetColumnSeparator("")
+ table.SetRowSeparator("")
+ table.SetHeaderLine(false)
+ table.SetBorder(false)
+ table.SetTablePadding("\t") // pad with tabs
+ table.SetNoWhiteSpace(true)
+ for _, template := range templates {
+ table.Append([]string{template.Name, template.ShortName, template.Description})
+ }
+ table.Render()
+ }
+ return nil
+}
diff --git a/v2/internal/templates/templates/angular-template/template.json b/v2/internal/templates/templates/angular-template/template.json
new file mode 100644
index 000000000..feab4ce94
--- /dev/null
+++ b/v2/internal/templates/templates/angular-template/template.json
@@ -0,0 +1,6 @@
+{
+ "name": "Angular",
+ "shortname": "angular",
+ "author": "bh90210 ",
+ "description": "Angular projects w/ @angular/cli - Note: in order to reach the cli use npx like this: npx ng"
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/create-react-app/template.json b/v2/internal/templates/templates/create-react-app/template.json
new file mode 100644
index 000000000..ea8b74f05
--- /dev/null
+++ b/v2/internal/templates/templates/create-react-app/template.json
@@ -0,0 +1,6 @@
+{
+ "name": "React JS",
+ "shortname": "react",
+ "author": "bh90210 ",
+ "description": "Create React App v3 standar tooling"
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/svelte-mui/basic.tmpl.go b/v2/internal/templates/templates/svelte-mui/basic.tmpl.go
new file mode 100644
index 000000000..b9b1d419e
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/basic.tmpl.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "fmt"
+
+ wails "github.com/wailsapp/wails/v2"
+)
+
+// Basic application struct
+type Basic struct {
+ runtime *wails.Runtime
+}
+
+// newBasic creates a new Basic application struct
+func newBasic() *Basic {
+ return &Basic{}
+}
+
+// WailsInit is called at application startup
+func (b *Basic) WailsInit(runtime *wails.Runtime) error {
+ // Perform your setup here
+ b.runtime = runtime
+ runtime.Window.SetTitle("{{.ProjectName}}")
+ return nil
+}
+
+// WailsShutdown is called at application termination
+func (b *Basic) WailsShutdown() {
+ // Perform your teardown here
+}
+
+// Greet returns a greeting for the given name
+func (b *Basic) Greet(name string) string {
+ return fmt.Sprintf("Hello %s!", name)
+}
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/.gitignore b/v2/internal/templates/templates/svelte-mui/frontend/.gitignore
new file mode 100644
index 000000000..da93220bc
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/frontend/.gitignore
@@ -0,0 +1,4 @@
+/node_modules/
+/public/build/
+
+.DS_Store
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/README.md b/v2/internal/templates/templates/svelte-mui/frontend/README.md
new file mode 100644
index 000000000..234b0742e
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/frontend/README.md
@@ -0,0 +1,93 @@
+*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
+
+---
+
+# svelte app
+
+This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
+
+To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
+
+```bash
+npx degit sveltejs/template svelte-app
+cd svelte-app
+```
+
+*Note that you will need to have [Node.js](https://nodejs.org) installed.*
+
+
+## Get started
+
+Install the dependencies...
+
+```bash
+cd svelte-app
+npm install
+```
+
+...then start [Rollup](https://rollupjs.org):
+
+```bash
+npm run dev
+```
+
+Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
+
+By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
+
+
+## Building and running in production mode
+
+To create an optimised version of the app:
+
+```bash
+npm run build
+```
+
+You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
+
+
+## Single-page app mode
+
+By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
+
+If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
+
+```js
+"start": "sirv public --single"
+```
+
+
+## Deploying to the web
+
+### With [now](https://zeit.co/now)
+
+Install `now` if you haven't already:
+
+```bash
+npm install -g now
+```
+
+Then, from within your project folder:
+
+```bash
+cd public
+now deploy --name my-project
+```
+
+As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon.
+
+### With [surge](https://surge.sh/)
+
+Install `surge` if you haven't already:
+
+```bash
+npm install -g surge
+```
+
+Then, from within your project folder:
+
+```bash
+npm run build
+surge public my-project.surge.sh
+```
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/package.json b/v2/internal/templates/templates/svelte-mui/frontend/package.json
new file mode 100644
index 000000000..7dd2f6cd2
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/frontend/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "svelte-app",
+ "version": "1.0.0",
+ "scripts": {
+ "build": "rollup -c",
+ "dev": "rollup -c -w",
+ "start": "sirv public",
+ "start:dev": "sirv public --single --host 0.0.0.0 --dev"
+ },
+ "devDependencies": {
+ "@rollup/plugin-commonjs": "^11.0.0",
+ "@rollup/plugin-node-resolve": "^7.0.0",
+ "focus-visible": "^5.0.2",
+ "rollup": "^1.20.0",
+ "rollup-plugin-livereload": "^1.0.0",
+ "rollup-plugin-svelte": "^5.0.3",
+ "rollup-plugin-terser": "^5.1.2",
+ "svelte": "^3.0.0",
+ "svelte-mui": "^0.3.3"
+ },
+ "dependencies": {
+ "sirv-cli": "^0.4.4"
+ }
+}
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/public/favicon.png b/v2/internal/templates/templates/svelte-mui/frontend/public/favicon.png
new file mode 100644
index 000000000..a03607d66
Binary files /dev/null and b/v2/internal/templates/templates/svelte-mui/frontend/public/favicon.png differ
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/public/index.html b/v2/internal/templates/templates/svelte-mui/frontend/public/index.html
new file mode 100644
index 000000000..b19ad70f2
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/frontend/public/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Svelte app
+
+
+
+
+
+
+
+
+
+
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/rollup.config.js b/v2/internal/templates/templates/svelte-mui/frontend/rollup.config.js
new file mode 100644
index 000000000..6d7a1aa13
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/frontend/rollup.config.js
@@ -0,0 +1,71 @@
+import svelte from 'rollup-plugin-svelte';
+import resolve from '@rollup/plugin-node-resolve';
+import commonjs from '@rollup/plugin-commonjs';
+import livereload from 'rollup-plugin-livereload';
+import { terser } from 'rollup-plugin-terser';
+
+const production = !process.env.ROLLUP_WATCH;
+
+export default {
+ input: 'src/main.js',
+ output: {
+ sourcemap: true,
+ format: 'iife',
+ name: 'app',
+ file: 'public/bundle.js'
+ },
+ plugins: [
+ svelte({
+ // enable run-time checks when not in production
+ dev: !production,
+ // we'll extract any component CSS out into
+ // a separate file - better for performance
+ css: css => {
+ css.write('public/bundle.css');
+ }
+ }),
+
+ // If you have external dependencies installed from
+ // npm, you'll most likely need these plugins. In
+ // some cases you'll need additional configuration -
+ // consult the documentation for details:
+ // https://github.com/rollup/plugins/tree/master/packages/commonjs
+ resolve({
+ browser: true,
+ dedupe: ['svelte']
+ }),
+ commonjs(),
+
+ // In dev mode, call `npm run start` once
+ // the bundle has been generated
+ !production && serve(),
+
+ // Watch the `public` directory and refresh the
+ // browser on changes when not in production
+ !production && livereload('public'),
+
+ // If we're building for production (npm run build
+ // instead of npm run dev), minify
+ production && terser()
+ ],
+ watch: {
+ clearScreen: false
+ }
+};
+
+function serve() {
+ let started = false;
+
+ return {
+ writeBundle() {
+ if (!started) {
+ started = true;
+
+ require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
+ stdio: ['ignore', 'inherit', 'inherit'],
+ shell: true
+ });
+ }
+ }
+ };
+}
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/src/App.svelte b/v2/internal/templates/templates/svelte-mui/frontend/src/App.svelte
new file mode 100644
index 000000000..c638d87b6
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/frontend/src/App.svelte
@@ -0,0 +1,51 @@
+Hello {name}!
+
+
+ {response}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v2/internal/templates/templates/svelte-mui/frontend/src/main.js b/v2/internal/templates/templates/svelte-mui/frontend/src/main.js
new file mode 100644
index 000000000..a1b7fa274
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/frontend/src/main.js
@@ -0,0 +1,10 @@
+import App from './App.svelte';
+
+const app = new App({
+ target: document.body,
+ props: {
+ name: 'Wails User'
+ }
+});
+
+export default app;
diff --git a/v2/internal/templates/templates/svelte-mui/go.mod.tmpl b/v2/internal/templates/templates/svelte-mui/go.mod.tmpl
new file mode 100644
index 000000000..21bd6c6a2
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/go.mod.tmpl
@@ -0,0 +1,9 @@
+module test
+
+go 1.13
+
+require (
+ github.com/wailsapp/wails/v2 v2.0.0-alpha
+)
+
+replace github.com/wailsapp/wails/v2 v2.0.0-alpha => {{.WailsDirectory}}
diff --git a/v2/internal/templates/templates/svelte-mui/main.tmpl.go b/v2/internal/templates/templates/svelte-mui/main.tmpl.go
new file mode 100644
index 000000000..5656d03e3
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/main.tmpl.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+ wails "github.com/wailsapp/wails/v2"
+)
+
+func main() {
+
+ // Create application with options
+ app := wails.CreateApp("{{.ProjectName}}", 1024, 768)
+
+ app.Bind(newBasic())
+
+ app.Run()
+}
diff --git a/v2/internal/templates/templates/svelte-mui/template.json b/v2/internal/templates/templates/svelte-mui/template.json
new file mode 100644
index 000000000..3f93967d3
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/template.json
@@ -0,0 +1,7 @@
+{
+ "name": "Svelte + MUI3",
+ "shortname": "svelte-mui",
+ "author": "Travis McLane ",
+ "description": "A simple template using Svelte + Mui3",
+ "helpurl": "https://wails.app/help/templates/svelte-mui3"
+}
diff --git a/v2/internal/templates/templates/svelte-mui/wails.tmpl.json b/v2/internal/templates/templates/svelte-mui/wails.tmpl.json
new file mode 100644
index 000000000..8bca94173
--- /dev/null
+++ b/v2/internal/templates/templates/svelte-mui/wails.tmpl.json
@@ -0,0 +1,9 @@
+{
+ "name": "{{.ProjectName}}",
+ "outputfilename": "{{.BinaryName}}",
+ "html": "frontend/public/index.html",
+ "js": "frontend/public/bundle.js",
+ "css": "frontend/public/bundle.css",
+ "frontend:build": "npm run build",
+ "frontend:install": "npm install"
+}
diff --git a/v2/internal/templates/templates/vanilla/basic.tmpl.go b/v2/internal/templates/templates/vanilla/basic.tmpl.go
new file mode 100644
index 000000000..b9b1d419e
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/basic.tmpl.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "fmt"
+
+ wails "github.com/wailsapp/wails/v2"
+)
+
+// Basic application struct
+type Basic struct {
+ runtime *wails.Runtime
+}
+
+// newBasic creates a new Basic application struct
+func newBasic() *Basic {
+ return &Basic{}
+}
+
+// WailsInit is called at application startup
+func (b *Basic) WailsInit(runtime *wails.Runtime) error {
+ // Perform your setup here
+ b.runtime = runtime
+ runtime.Window.SetTitle("{{.ProjectName}}")
+ return nil
+}
+
+// WailsShutdown is called at application termination
+func (b *Basic) WailsShutdown() {
+ // Perform your teardown here
+}
+
+// Greet returns a greeting for the given name
+func (b *Basic) Greet(name string) string {
+ return fmt.Sprintf("Hello %s!", name)
+}
diff --git a/v2/internal/templates/templates/vanilla/frontend/index.html b/v2/internal/templates/templates/vanilla/frontend/index.html
new file mode 100644
index 000000000..d39fbc6d6
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/frontend/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vanilla/frontend/main.css b/v2/internal/templates/templates/vanilla/frontend/main.css
new file mode 100644
index 000000000..f399ab518
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/frontend/main.css
@@ -0,0 +1,18 @@
+
+html {
+ text-align: center;
+}
+
+#input {
+ margin: 3rem;
+}
+
+#logo {
+ width: 50%;
+ height: 50%;
+ margin: auto;
+ display: block;
+ background-position: center;
+ background-size: cover;
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAABS9BJREFUeJzsnYebU8X7t98/5Le7KdvoLL0XpQjYlWJXpIgFuyIWigVQlCJWvooNsAEqNmyIqIDSVJqK9A5LX1i25Z1kzplMzsmG7G6Sk+Tc9zXXkg2bU+ZMZp7PzDPP8/8CAAAAAADgGv6f0xcAAAAAAACpAwEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQAAAAAA4CIQAAAAAAAALgIBAAAAAADgIhAAAAAAAAAuAgEAAAAAAOAiEAAAAAAAAC4CAQDgdmpqaiwvspiaUIl4x3bX9asQN9QeAABkBwgAAAgi7Vf3WLEx7P54fg1E6gT31BsAAGQBCAAAt6MbrxiydZ31p8YAACDjQAAAABhEnfW3CKTaHIRQAgAAkCkgAADcTm0GrkuIauVr/x0q1TWHDx5at2btV198+b/XZ0+dOnXevHk//vjjli1bTpw4Yf0IAABAeoMAAIAgrjJha9M5wXerqnfv3LV82U/vvPX2hHHjh958S8/uPfJ9/pbNWwy/ddhrr7z6x7r1VRWVqb9mAACARIEAAACDqqqqQFYrAetkf02g7NTpDX/+9fGHHz395FPC1u91wYXC1vfmeTy5eeJnYX7BkEGDX3zxxfXr11dXVzt56QAAAIkDAQDgdk6cOPH555+vWbOmvLzc6WtJIsLur6ys/Oeffz777LMXpj4/Ytjwnt17+L0+Yej78zw+j1e8kKVj+w6PPDzm2yXfCHkQQw5lsVICAIDsBgEAkP1E3bdaea7iu2++veeee5599lnpyB5IJ6O2tiup0/tVVVX/bPn7w/c/eOKxx6+4/NLGhQVeT64s/rxg8eXm5HvyxAth9w/o13/m9BkbNmxI5G0AAACkHwgAgMzmvCa7/Q82/rVh3ONPlJSU3HHHHbt27Yr/UIklRhQdi2KJ8x7lz23bti38eMGEceOvvvKqRkXFamrfk5cj7H5fbp6c7/fk5omfogwZNHjOG2/u3b1HbvaN53QAAAAZDQIAIFOJ7Z2i28Ty5+mTp+bPnXfZJZcKa7hf34tWrlyZskutjTplH6vtzyoqKtauXvO/12ffNmJkm1atlSePtO/zcnKVT7/h7RN6feXlV7z15pyDBw8m8n4AAAAyAQQAQEYSf7xO8Wd/rv/j4Qcfatq4iTCIWzRr/u7b71RXhvf7pm08+9rkgXinrKxs+bKfnpvy7OCBgxoVFSsT3x8uQfceUfymGJB/I5TP7NdeD873n+/U6VMPAAAAiQUBAJBV6Gbr2bIzH77/gTB5heGbH3J6GTvmkaNHj6aPaWsx8WN4AQVqAlUVlat/+33m9BlDBg0u8OcbM/3RfPpDdn+udO6X3j6tS1pNHD9h04aN8VxPbb8CAABkBwgAgCxBt5j37t377OQpJS1aBj3dQ17vfS/s9fuq3wIWo7am9gRYKae2LLxbt26dPXv20JtvadKosZzIF2JGTvOLF8r0l+a+1AByU29wQcDrG37rsCVffV1RUWE5S9SFBf0n1j8AAGQrCACAjEc3VVf88uvI4SNkMHtZigoKp78w7dy5c/aPnCcJbpKJcd7yM2d//GHpuMef6NKps+m7b53gN4tHOvkoL3/5om3rNi9MfX7fvn1RzxjPLSMAAAAgW0EAAGQquoVaUVHxySef9O/fX3q9+0yfn8EDB23959/gTH/EJ42P10Q7WuoNX3XGvXv3vvv2O7feMrS4sMgTWrhQRfr5+Lx5ebn/J35qnj+G0S9dfcRrccuLP/1MTfnbzxLPlQAAAGQxCACAzObM6bK357zVtXMX0zEmOE0ubOEWzZrPnzvPKYu2NgebqGzduvWVl16+9OJLVJQe3fT3GV5MubqHjygyrKda6Cjw5999991btmzBiAcAAIgNAgAgjajNNSWqx05paenzz00tadHSmAIP2c1yXnzozbfs37sv4ATx29+bNmwU19+3dx9x8XLyXt5FgdenawApbNSUv88rXYDM9/M8jYsbPTXxyX179tbp7AAAAK4FAQCQvkTVAIKDBw8+OWFiUUGhmv8Ozp2HnGBaNG/68YcfBZ18nLaEa1sE2Lp169SpU7t37WZE6w95K8lpfvFCvrasAOguQOpT4s02LUteenHWiWPHa0ycuFEAAIAMAwEAkHbEMGQPHTg4cfyE4sIitflVmsXSH+amG68/cOCA/VCpsYxrM8Hlm/v27Xvttdcu7j/ASNGlWfnS9Lc7/6hpfuUCJBcKZDaDl2e9dOZ0WdRzAQAAQAwQAABph7Riq6urA5oFv2/PXmH6FxUUSocZFfhSTo0LSfD2nLcC1Y5t5LVcvKLs1OkP5r8/eOAgmZdXpeOVTvz6dl61qVcpAbU44DeiAAX/q1mTps8/N/X48eO63iB+PwAAQPwgAADSgtqi4IufR4+UPjlhYmF+QdBF3punB7yXJvWAfv3/+3drrCxaqacm8NvKVQ898KAM3u8znXzUHL8K6SPtfmNHrykA9GUBpQGEyHlq4pOlh4+YZ4ju5YT1DwAAEBsEAEA6Iq3Y06dPT39hWtPGTaSrjwyBb6a5NbbAPjt5SmVlpf2zTkXzPHTg4MuzXrqgR0+VplcY96KEU/OGrl8580gNI4N4+kyHH+X5owTDHaNu37VjZzwXEEADAAAAxAQBAJCOVFRUvPHGGyUlJV7T0V+azgVeI9CnsIzbtWn7y/KfA7XYuyk2gqurq3/99dcRI4I5yILWfF6O8lCSfj5qo4JRwr5AET4/9v0AA/petHrlqoDNvsftBwAAoH4gAABSirRSY1mr1TWLP/2sU4eOeqbbiKD4oSnzm2666ciRI+HDJsH8je6VZPuzUydOznnjzd49L7BY87GL10xZIG9TqAXTBShXygZPbl7Txk1mv/Z6VVVVlMto8P3W9TgxlhdiBCCqt0qp62pGjD9DGgEAgAUEAEAasXbt2isvv8KMdCkn0T3KD146yeT7/MIslluEdRJlE8d+X3/99+YtY8c80ri4kcraK6+zwOs7rxLwGvt6c80NvrmaAAgmBLjlppv37t5TvwuO+mf2Py4rK/vvv/9Wr1791VdffTD//VdffmXKpMniju4Ydbs4+5BBgwf069+ze48unTo3b9pMlCaNGntMAWYpBf78Zk2alrRo2bljp+5du/Xre9EVl11+/bXX3X7bqIcffOjJCROnTZs2Z86cRYsWLV++fNOmTYcOHbI4btXjHjH6AQCgfiAAAFKLaZhZrGph7I6+8y7pDS9/6h7/ftPQ7Ni+w9rVa6yHTLTTv/2Alun/n35cdtMNN8pLkluTVchOyxbe2kroI6H7yjEj/5iGdYtmzRd89HGcl1drymG5R7i6prqyauf2HcuX/SRM/BemPn//vfcNHjjogh49hTWvrt9u0Kty3huJWtRH1DYGw+XJ65MSQoZyatWypE+v3jffeJNQHTOnz1j48YJflv+8/b9tFeXn7HetS77YiZYtNYMYAAAACwgAgFRjsczOlp15/rmpjYqKLXanOUEemh0P2ak3XHd9aWlpQPM5SbFtV1FRIUzzi/r0NfbsmiGJZGreeDx/VJHbgi3rG+I2rx1yzf79++t9hSePn1jz++r5c+dNHD9h2NBbe11wYYE/X7fy7eJEvbbsPdDCj4aLlGRyJ4a9yHpQn7UrB0uGY3XXeozU4E5or69bl67XX3vdE489/ub/3vjum2//+++/qqqq4OOOfOC1aaHY20KQBAAALgcBAJBSLLbXN18v6dqxk18z/U33mLCLfHBDrc8//YVpMSy8hOuB6kDEAU+fPPXaK6+2a9NWrVHoIX1CixVWz5/YE+fBbcEhcznfjP8jLHVxiurq6tjT2xYOHTi49PsfZkybPmLY8I7tO4StfMueYzMekdp1oF7I15Y/U5+VyQr0d+Sv+sf1o6mP6EdTextUCFf5EbXzwV70BQQpYET9COl1z+i7Z818UUiCndt3BOpu05MvGQAAAggAAKfYtWPnLTfd7DOs5HBUHOUSY0TRyfO0bN7ipx+XBc63ozRRhp3Fpjxx4sS0519o1bJE2qOR0Xv0+e+IGe7zes7IFQB1wPZt2/2+6rfY1xP6JShFhMUv5NDwW4d1aNdeF06RJYq1HXwRMs3t2kBFK7IY7rrdb9EG+gdrO6zlff2FRRioqzWVVZ6+NBHOn6CVxsWNrr7yqgnjxi9asFAuEcSqOgAAABMEAECqKS8vnzFtelFBoWnk5UrDNC/3/wzjz+NV22F7X9gravz7pNp28uCHDx9+dvIU6SuvLGnpsqJZpRGuLyp1l26z1lJylewZPHCQjGgU/aZqAsdKj375+RcTx08Y0K+/njdAriSoPAOWKXm5iUL/qfInxF/sawi1zdkbfxC5S9hjzt/bFZGqQMtign1VQb6wr11ErHLkeZo2biJq8skJE79Y/Pn+vftS2WAAACCzQAAApJTfVq7q2b2HcpLRnU/8Po8+QywsvDtvG1VWVhaIb7K/IRaexbno6NGjUyZNVttkdbvTvjtWbVOuYzHkxEMPPFh5rsJ+PWfPnl26dOn4J8YJCaQ8+PVkYXYDXbfO1Wy6zW4+j2+S/Tpr8/appXjiP4WSf5Y9BiomkuFupC1N1K4Twiswsrq6du5y912j33333c2bN+uLAygBAABAAAA0lOiu+dq/8uXJkycffWRsxOyybaZZZvgKvuP1zZo1K1acxzpdTIyLj9w/cPL4iRnTpjdt3KTuBn2txeIXpPzaRXn15VcsV7v9v21z3njzphtuLC4sSuA1pLDo6w/hNRB7rCFRyd27drvssstuvPHG0aNH33fffRMnTpwwYcLkyZOnmTz77LMTQ4wfP/7+++8fNWrU0JtvGTJocP+L+vXo1r1l8xZq37BXC6Okn0uevaRZ81tvGfq/12dv3rgpRmOorrVZAQBAVoEAAGgQ5zWy5R98u+Sb9m3bqSg0dsNR+fz4Q6bhD99935BrKC8vX7Vq1enTp+O8VPFf4iOzZr7YvGkzM/Fwgi1j3frPD2Uz+PjDj9Qlbdm0edrzL/Tt3ccy019bdaVtUesDuhUunXPGPf7EO2+9/cvyn3du3xEM9FlTu4yL76kJqqqqjh07tm3btnVr1i756uv33nl3+gvTxo55ZMSw4ZdefEnb1m3sgU1btSy58/Y7Ppj//p5du+t3UgAAyHQQAAANpbaMuTLiyvGjx+4YdbvHNH/DFn/op/Zm0D1dvNm5Y6dNGzbGcVbDfLSffenSpXPmzDGiRka7Tsv7lZWVwhxUG2pVhP5EFT0ovrzfIn++sFbFqbdv3y7s/gt69PSYDkUqTH4cuwjSsSg7uzC/4OYbb5rzxptRPXDiCeIUZ3bh2H9QUVGxa9eulStXCrn1wtTn7737nisvv6JNq9ZSn3Tv2u3xRx9b+v0P5WfORj24I9FmAQAg2SAAABKG3U76/tvv2rZuoxmIYcPao1m3Kl3U5ZdedujQodqOFnmy4I+IoJnVNfv37ht9510ffPBBjEialv8SV9i3dx9pc8tViPr69Mey/n2aQS+s/8WLPpk/d57Keez3+iK8oXJy5R/rDi2OW/bxC4BLL75ECKpTJ05aqjp28mZlap83Cmpso/z8zSZUzpad2bxx07dLvpn92usPPPDADTfccP/998+bN+/AgQP26wQAgCwDAQCQSJT5Jey/hx540BPeKZunorUoS1GGwDfyQOXmCdtdmGX1Pu/bc97q0a17bXmCrW9WVf+9ecs1g4dIx/HQdebp4TITbhmr6fwObdo2a9TYbwYV9ZjJsOzRPC0JvDKiiEsdeNXVy5b+aEn53MBMvbowqF8s//MuL4h3jhw5smrVqtWrVx8+fDh2umUAAMhoEAAACUZYSyt/XdE5lN7LHqxGNxZVyEjxYuqzz1VXVqkjxH8u8XPLli1XXHZ5rwsu3L1zV9QjRJhx1UGvpMcffSw47+4xXNX1kPl6WM+GF0t6Xb/m9SRfqERgWgrksDGtFgEct+zjLD5T1Ikn8vNPy+tq98fepxFPe4i9MqBm9+NJFYzdDwCQrSAAABKAsq6qqqomPzNJhaos8Pp8pmuNblgrw1c6wMx99z11nDpZXRUVFdNfmFbgz79m8JCTJ60+J3YzrrKy8u05b8noMXokfj18ZPJm3OVZ1J6H8H7ZiPCdYZGgb1113LKPuxj5gKVuGTb01m3btsVoM7Ffx25v8X8kht0fW5agAQAAshIEAEDC2Llz56UXXxLO6StNajO+p4oTr+xgoRMaFRV/8/WS+kVf3PjXhgH9+ouz3H/vfUIJWKSDtPn099atWycTaXm0BF6WlFVJ8rm3b+fV0x3EyCicWRsApABQgfzlskZhfsEzTz196tSpQN1n/WNTpw/WyeMIAACyGwQAQB0IGk+a+VSj3gwEFn68oEmjxqazimHo69HZVVItlVGrpEXLNWvictk39m5WG5O1FVWV056bmu/zi9NNf2FaDIcN+ebx48cfG/NIfmYG1syO0rpVy88Xf2pvNoFoCzXgcuraGOrnTsZqD4CbQQAA1A372Hn65Kn77rlXzaD7csObaHUHd3My2wgS36VT5//+3RrjFLW9+e+//w4YMED5Dp132P5k4aLWJa38ms+9npnLccvYJUWueNxy08179+61P1NsL7AQY/dIAo+cwMMCQGaBAACIl6gj5b9//9OnV28VykatAPi0za+6zS29w3tf2Gv/3n32w0Z5XRN+s7q6+o3Z/yvMLxDHKfDnf/n5F3bfIf0I+/bsvfWWoV4z4pBPC0kUTMVlLghQUiYAZFKwBR99XFenf3APUduGrgE2/PnX9Bem/e/12aIh/fjD0nVr1h46cDC4PFgTjO5lzxBy3gZm/8v6RZoCgAwCAQBQN8LjYk3g00WfSLcf5eqjrGrlvO6LjPcv3ry4/4CjR0qjHDDar4qDBw/ecN318phNioqDgSZr/6yQCnPffU/YmtLdX4/qE96BwPR/ygWAagMjhg0/cuRIrU0LoBZ+/mm5+F4bjoVmFC/xs8Cf371rt+uuufaB++5/ccbMLxZ/vnnjpnNny+NcSahtdzgAZCsIAIB4sYTTeeKxx0Px+y3bWA0LW0X3V9P/8p2BV1196sTJqEeOMQZ/9823rUtaeXOCCwti+F/xy68xrnPfnr3XDrlGC54TsblWXQ8CIJVF2Gr5njy1CCMeTYd27X/9NfwcsbcgKnrDWPzpZ8LQN4z+vGDKDn1/kSVvhvQSvKBHzztG3T5z+gzRhxw5dNjBGwGAtAIBAFA3xHh8cP+Byy65VM7A2YLYGKa2EgDqV1FuvP6GsrKyeM6i4jaWl5ePGzdOjuj5Iet/3Zq1Ua9Kvljw0cdNGjWWoX5MczMi/Kh6oTsmUZJdzAxr4XeCizNe3wtTn6+oqLA/R3AntTaAmsA7b70tc2arrUQyloDcZaTWIc3MHhE7/tV/Cdk5cviIV156+fdVv507Wy7jCgCAC0EAANSFmsDa1WvatWkbNO7FoJqXY1sBCLt5eCJjWQ6/dZg09eJ0yRU/d+zYcfGAfjKKqDhCsyZN169dZz+C/PXIocNiaNfjZuZrAYjUO37N+mcbQMpKqMJzTCWWqxtwQhYeO3YsWS0WMo2ojvhCKOblyJ1FKqNIOIyvJYZvKBRB8A9Ci05GUFrlhKbSbxfmF1x1xZWTnn7mxx+WBnOQ14R3HBGZCiDrQQAAxIsYDj98/wMxappxfuSAmqun0VWprPQUtuLFsKG36hO9sc8if3755ZdNmzaVw7wQG40bFa1bty7qHwuWL1/eoV17e8ZcNTtoz8hLSXnR7bCwABAPqFuXrhs3bkxsc4XsoLq6esyYMZHJBMO9jSfX6gVkFE9u1KJao0pEKEuBP3/wwEEzp89Yu3pNZWWl0zcNAEkHAQAQgSVGe/j9isqJT4yzW3VyHt0+AOvx/uOx/qsDERsMJowbr/vyiuH5l+U/2y9M/Fp5rmLS08/4Il19KJlTjHbSqKg4GNbJ3iCJF+QqIn1yzpadGTlimHIsTF7Rc2+3Lml1/733ff7Z4pMnw7uVYrfDGJGLACA9QQAAWLGH3hMD4Q3XXKsGS30TrRw4lc+P+hm06nzG3L+w0aMOilE9eQ4ePDjwqquVa6/cyffVF18GoiX33b1792WXXKpnGHDanKXUregposVDfO2VV5PbuCE9iCc4z4kTJ64dco3Pm5caYZ9vpitRPU++z3/dNdfOe29u6eEjUa/8vDcFAGkLAgAgAvtgvGfX7t4X9tKHSb8ZRlMuo1sGUdOxPrg6r+b+7bZ+VEnw+6rf2rVpq0fuFwf5YP770S40GBqoRbPmytfIvs2UkglF+XMHrS5Rxo55pKqqKoA55QJiROQ8ffLU5Zdeppx5UtMaLVuK5Qqk3K0+6OqBb705Z//+/ee9BbftH3Db/ULWgAAAqBXRp69fu65Nq9Z+W1QNw/MnJ1eOl5boOvKF2vWrH7DWqP81gXnvzc33+c3sAcbE8KRJk+wXJgzEyc9MsgT9MLciOG7RUupUrHvHxTMVLae8vNzaQiCLiJrhS3H0SOlFffoabjmeXE9eKlYAVLwg+aseTkB5NorGed011y78eEFZWRmuPoLNmzfrv7q5KiDjQAAARKCPal999VVxYZFljNQj5yjvfyUA5JqAGCZvvP6GynOG9X/eMaGysvLxx8bKkV6Z/uLXkSOGVVdXW42Do0evu+46ORjn5eSqVQjzg45btJQ6lfC2TrmAIw2va4dcc/r06cS3b0gnotrQe/fulUuORsqIVK0A5EfmLZFFdkrypx7toEmjxg/cd/+KX36Vq1WWO3KPHbxmzZpx48bpmyUAMgUEAEAUxAA254039QCaUef4/WYC4PB4GSpXX3mVEVbPdlj7kH/86LEhgwb7vHlacJjgQfr06l126rT+WfFz88ZNXTp1tgT39CEAMrZIo0qGa1QGVjDBkyf3issu132vA26yq1yFbjTv3rmrR7fu3ogOJ3Xfa0uUsKALYl6O7Jpk76RvWfGHxKrojl6e9dLhw4dru6NAtrfbL774omvXrqtWrcru24TsAwEAYEX0489OnuLRJvW9kaOjvg6gG+LSg3ZAv/5yQiie8WDb1v+6d+2mZ/aRw2qTRo23/vOvuh754usvv2pc3MgIDaQNyVpKMuctWkodS0QSCUtI2Yv69D16pBTDwiVs3769Y4d2KmRnqAsyEkgnux2qTU3eSIcfPbaBWh+Qxa9lHijML7jrjjtX/rpC3Yt7Gq2405dfftnv9c2YNp0IqpBBIAAAIqiqqrr//vv1rb1mVB/r3Jh8obbNyb+8sOcFpaWl+gGDA2EtQ+Gvv/4qd/FGGoLBYy5asDAQOYX20ouzIsSGLcI3O4AzsxgqTpr+QTmnJQoQLy675FIcDNzApk2bWrdurZ67oQHyUrqsFy2csVF8tWgDyxF69er17rvvnjlzxunqTCmif777rtGiQm666abjx487fTkAcYEAAAgjxq3htww1BrzIsc2rDYF2A07G++/UoeO+ffviPNfCjxcU5hdEZhDLlSe9/bZR+l9WVlY+9MCDXm2Dge0aKNlWlK+FeNyXX3pZ1IjsbvO3zlDieUxbNm1u3aplViziBW+hpEXL55+bandgi5pmOAsasLiF8vLyAf36iw66W9fO//29JZAV9wXZDQIA3IulgxY21uCBg6KZYtaVcduAF7T+xZi3bet/sU+kXsjp/NCR85TPd2ga2Nu8abODBw+qz5aVlV1/7XX6efXoHJTsLRErQldefoVlVhXrP2vYsmVL65JW2eLFZwS0FY22uLDokYfH1NYrSmIEQs049uza3bJ5C/EcmzZptHTpUv2/MvSOILtBAIB70U2o40ePXXHZ5bZttWGnf6/m8OrXooLKAU8Mdb+v+i1g6+jtodyrqqrGjnlECx4a4QEi3p8/d57646NHSi+75FJf5CaEaCKEkm1F2xZsBHsdfuswS8QVexuDNMQe/Ffn781bWrUs8YTCffq8WeDFlyuzl+Tl5MqOS/wcMWz4hg0b7NWiv8j0Ziyvf9nSH+WjzPf5Zf6WbFrogCwDAQBgRN3Oy/2/UNJN07k/xwjNbgTjM3Nk+u05ATzeLz//wnLMqEqgvLx85PARync2tKMux8wDlSPe7Nu7jzLyDuzb3+uCC3VHW5UazKeFIqVkack1xWF4t6WQjrU1MEhzopq5wvpvXdJKPvFUJvxKQQnnRJfLp57cYUNv3fjXBr02aquWTMS4hZrAlEmT5e5tcdczpk2P+F+AdAIBAK5G9MsH9x+4oEfPoFXtDSfS0uf+1Qs9SLbaDyA+OOeNNwNxdPEnT54cMmiwNn+fayni/U8//VT+8fbt2zt16Gif6fdpGTopWV0s0YGM9jZz5sxAFplNbmbLluDcv1+t7GWLADBjBOkxQ3ONcKJ5nuG3DpOrAVk5NS7upbKy8qorrlTOe0889jjfVkhPEADgavbv3Sejbntt8bZ1iz+UZ9crU//mm8l3pQZ4csLE2KdQzjwX9x8g3Xyld4c+OsrztmnVWkaR2/7fto7tO6gUY5bsY34EgAuKWhdS0YEMQyo379NFn1haF6Q/Fkeg4K7fklYebb1RbgRyvOElqBhNV+/lgouceTmyWxt95117du2OWkspfCYJwH7B4p2dO3c2adRYhUt69JGxGXdf4AYQAOBSRI8srP+e3XtIxx6vzbEnIttXbl7EmoDXJ+f+bxsxUnfLtnv8Sw7s29+3dx8V21G5d0eOjnkTxo0Xn5LWv99cQNf3HmD3u6dEbSRSDDQqKt60YWN1dXXgfP7lkG4Y6fw2B2P+hGP4il7Fm6fcvbKg6FntdC9H2Z7lLRf485+a+GTUoJmZ2KTV91H++sH893XXzbvvGh11Aw+AgyAAwKXs37+/e9duUaNqKj8fy2tVpDl+cf8BwXS/kRirvdo7e/fu7dGtuxnPO1eP8q4nfhIHXPLV1zu37+jQrr06qbL79SDcWWMlUGIWWwsJRYWXtlTXzl2OHz2Wwq8LJIwdO3a0b99eT/ksS+jJOt7qElNCm6lyLA3YsuYpNz23aNHi9ddfr6ioyESjX1Jb4IdbwxGlc0QHPmbMmMy9R8hKEADgCiK8MGuCnj8XdOvui9zRG6dB5g9Z4W1btzmwb7/9FJZ3pEFvcfWpraz65dfunbv43WLiW7dAmLZC+G/yzWRDwZKXo2dHsudBsxbb4onaw23f4JFRxUgOMGTQ4OpK5hTThThtO9HzdO3SKSvCfSayPXfr0vWrL74MVGsrqHqVVmek3SwGiOZNm6npHtFrTRw/IcAGHkgbEACQ5djdco6VHu19YS9fhPet1xdpGkYdqKR5KkzJooLCP9atj30W5cwTCveZo82B1ToWdhZ/nJEmaf0Hfn1q0MxnHNZj+jKIZRJRvlCTi/qhjKOZGZotKzzyaPYMpplTjJ0A4i4mPf2MvRFiWDiIfWOr/jhKS0uDcb2yJN5/Yor+bb3x+hu2bdsWowIzjnnvzQ2v/YbucerUqYFsTIUGmQgCANzFyeMngptxbbZ+HJa3MfcvDMqFHy8IxAz5L9i5fYdp/YeN1Nib/OyXFPfqROYVfTo/uKMi9NMMxGTVBqFBNPxZu0iw16S+AqB/xLJQII4sT+14hcRZpCO12oa+fNlP9kbOxgBHqE2AyXdOnzw1oF9/GW0sizb7NrSoDOhSqBcVFE57/oXy8nK9AqV7fUZSXXPVFVfqqRvFi/lz5/H1hHQAAQAu4szpskFXD1T7fWWkf1mi+vpHGa7yPMFl3Mje2z6Rs3379g7t2qto/TLef9BuyzuPm2+B1xd7+0HWFN2TR74wZIAt7o38L4s/j9oa4TPdhPT39V+NbERhGRBWF8r6zyCDzAghZd51+7btSktLo7Z2jIzUY58UkO+InkcYgkZO8WwJ95mQojKg+80lO/GzW5euS7//QXazGdqM1aPftGGj7KbCnbnXF7y7yD926DLB1SAAIPuR3WtF+bmbbrhRTQnXdX5dWpPXDrmmqqIyENPdYv/efZ07dtLcVLz6EWLadl5LuKEsXgHQb03deL6219lSiguLenbvcf2119191+gJ48bPmvnie++8u+Cjj7/+8qtlS39c+euKVStWip8//7T8m6+XLPx4wfy586a/MO3hBx8SD108Dp8ZvFWdNEOr11gS0XaH33LTzcpJGksiHbCswFRWVl53zbWqnUu563hDSpOiVgD8kZnORbltxMhDhw6pKnXoYSaAsWMe0d0OxW02Kir+888/Axl+X5DpIADAFdRUVd8x6nZl/6kQn8oKPO9cu7C0OrRrX3r4iHHAWtw3hfXfvWs3Pc6P9Pu3G6DRbLs8/fLiuapML0rwqOwK8qeowxHDhj87ecrHH3605vfVotrPO1LW6vcS3E4YnIL9c/0fb705587b72jTqrWxelC3XeBpUfRsFcpUenvOW7G9zyGV6I+gurJKtGSv1udkV7z/hha5AqB/E439+nnBzOglLVqKHiCjW7K4+BPHjrdqWeIx86PJaY6O7TscPnjI6asDV4MAAFcwcfyE0AbTPG/O/9ksqrCRHcMWLPDnr/7t90BMh9SjR0ov6tNXt/6NSH9mCBrf+WzNOPYiZ0/RXXR6du9xz+i735j9P1HJp0+eEia7WmNJ4PCvDrVpw8Zpz7/Qr+9F+j6BjCghM0JpAMNvqlFx4bZt2/QaY0NwmjDm4Qelm1le7v95TXvX8VaUPsVj2MTh5MERW3RC0wFDb75l/959gUxuz3Pffc8I5axtAb/y8ivkejKAIyAAIPt55ZVXvGFn07zaQnDE9rmf88abgZgj0KlTpy6/9DIx2KtkN3KtXyW/lIsAMcZCX718kzK0iAoRYumpiU9+/eVXR44cSd7Tt7plR/7Xxo0bJ4wbX9KipeMVEn+96RmCVfSkgQMHCmmauRZS1qBLrxkzZqhlQGXUEgJILx7T7lc57yIrKlf+TcvmLRYtWuT0s60/wtDv2b2H1wxCYCwL5+Y98vAYpy8N3AsCALKHqM4Pn3y8QI40cUZ/N3ageo1gHdJ2Hzl8hCXRo4WKioprBg9R0SqzeIy3LJioeDtyQ609Tr/6lFz4btem7QP33b9owUJh9KfJLLW4gLNnz7799tvdunTVfLRyTV/tiCLXkdJwEldc2Nx333O2JiFMTeD9efNdspSXvGKpwLtG3X7sWGbkv7MHhl786Weyn7RIQdFOYn8cIEkgACBLsFj/8ueyZcuK/Pm1DSe1lHB0eem007ljp+NHj8XokauqqrQNBlESWmVT8Wqh9CMC8oRm7/TQPSohl6jDgVdd/fqrr23euEn37UlJu4gLdUkff/hRpw4dTU+tiI0ctoSmzj8LvQjDomXzFgcPHnS6LiHYkL5d8o3f63O8VWRH0RdFxdfzt5Wrzlv/gbTpYcKOeVXV/fpeZMwrmesA4r6KCgr//vvv2j4IkDwQAJCdiN5z6z//tmjWXB9F5EASWwbIiRlpwYu/LMwvWLNmjeXIlnMFNxiEI/xk+QqAPedu1KgmojZE1d18403z5s3TPXzSZFSLsVO2srLyf6/Pbtq4SeTTjMhBlpabOIN69Z7Rd9d2X5AaRM2vXb2mUVFx7B3/lDiLxTEyuJ3a55/67HNVVVWWao/6LFL12OOgJiBkofICUsOEeKdPr95lZWUBdvBDakEAQPag956lpaVdOnXWhxB/fJH1LU66L704q7a+WL7/2iuvyklu81w5bhAAah5L85Mx9jkIu/+2ESMXf/qZ3Muro/ZP61t7nRrzYlsMhw8euvuu0UEHJzNZhMoxnK55A4xso2p+FBvCEbZu3dqyeQtvpkWXSucSGRrB2FU16OqBB/cfCNT+RU6T9m+5kssuudQXygVhbn3OkXvDHn7wobRauAA3gACAbEN0oOXl5VdfeZW/9m21MWSA9Frxh8bvwQMHVZ6rCAQC9i5ZdtNLliyRsR3kyKS8RLJYANhrz/jV67vhuus/fP+DU6dORbXpYwxsTo15Eee1XYL436Xf/9CuTVtvhAkSsbchnYohAAb065/BmVMznAP79nfu2CmDgkqlf7EERlNRHERTb9WyxJIJWw+BlZ589cWXntywM6HcTSQXkMV/RRlpAJIGAgCyCtn1333XaHMMzvVrAkBPOFXbeKOc2ps3bbZ39x79sIFIk3HDn38VFxaZ2b5CpmEwxqjyF3d+7EzqkKyq67JLLp3zxpvK+zz2PFa6jc3267Fc/7HSo7ffNkqFChWPOD0FgIo9JS5VyLCotwbJI7iVvOzMpRdfojx/9ETjlHoUS0dt9t4ReTB8Hu+LM2aqRHiBNG728sKEOO/etVvo+o29Uoae8eS2LmklehvWASBlIAAgewh2mjWBV156WXfHj8fv31KkAPjsk0+jHN9k/959Hdq1VztflX9I2m4STVRRe3/btm7zzFNP/7PlbzVrFdWYPu9IlvqhLv5LUoPxu2+/U1RQ6EnjFQDdQ0m0zDNnziS9HkGnumbUyNtUHDC5v5NtAA1oz7H6bSGu1JYAUUYMG37q1KlA+vnQW2MQh359e85bIZfRXBU5TYkB0YQculJwIwgAyCqWLf1RBt9QIaVVjsn4o+yLEeX220bFOMuZ02UD+vXXo1ZHCxfj/CCajFLgzx85fMS3S76prKxzCpt0Xhao0ZyA9Ek49XPDn3916tDREt40fYqKpC43ZggZ7FhVupIpkyabqb61oLFOt4qMLlGtfz2bu/qDYCbBnj23bt0aSI/OJCrqws6WnWnZvIXaTWSmBTD2U3322WdpewuQZSAAIHvYtm1b86bNDFcNs8QYXfJDu6/kZIwe+rNNq9ZRl2KNNdzKquG3DvPlKnd/50fKhhXjLoxEleGwnuFqlJZNj27dX335ldLS0jg9+7OPw4cPD7zqaqOKTOEnW44R3tu55ygfU4HXJzMzCAvj5PETAZc9oNQjVx0/+uBDp7/FlNxmTRv/9OOyiEcT+SJ9eHbyFI+mXpRulBsbYkedBkgUCADISOzW+alTp3pdcKHy1Zbr7+fNvGtaumE7Xrzz7ZJvYmzGem7Ks5ERPx0f+epcIpz4PbnKd9wfkgHG4rupBwr8+bffNurnn5YHHW1r0nE0TSUVFRX33n2PLgCks4exFuTgM5VqVi5zhV7PmDbd4hvt8meXJFatWFmgJRuhOFRC30ev76233ooR4CtNvgK7duwMbyuKFADizbFjHrEvQirS5BYgC0AAQOZh7wGrq6tvGzHSYxr0ssReAdDD9cjVZPnx0XfeFdX6lyf95usl8rDS5kvDjLC1lag76vymISsnsHV3KVEbXTp1fnnWS4cOHCQ2hY5obEIESiM70p3MSUFormV5lEnRrEnTUydOymt27aJNwrHYZNv/29ayeQvC/jheDIfM0Bfh8Ucf0x0U7Y3f2a+APPt111xrWUsMDijmovTa1WuifoovLyQQBABkKnpX+L/XZyu7XFq6+eYEdm0DhhGD2fCcDva5opS0aFlaWlrbGcVg37RxE9lHqyHH8ZGvrsWytU4PXRpUAiEVdOstQ5cuXUo0yUDtUcbfenOO1hLkhkUn24O+9qV+VTsBLNYDZkRDULV34tjxC3r0xNE/PUo45IN4IsNvHXbmzJl01r2fLFykBEA4w6Cp3vtf1K8em6wA6gQCADKcmsDvq36TG3/1Im36+FYAcpWbu+iUrYc3HSfOnC7rfWEv+ZdmUNG8UNxPx0e+uhXbOoAZyMib17i40fgnxm3bti2QEZk1nSBcAzWBRQsWiobnC6lNx/eEWBzeVKSms2WEA0o8ohlUV1bddMON+oYZiqNFbsgxZjfEd+HSiy+ROcjT0PlNXM+5c+fk2pHmSZjnNZfyxIu33pyju1yyAgAJBwEAGYzoDQ8fPCSsHNGH6nF+5JAQOwyfFq4nV8aTFsO5/fgqeLMMBi+9LHxmSPhMXAFQfj6a2erp1qXr7NdeD+bujbx9+69uG4FiueHWBL5d8k2+z68lnXBSAPgjo11J23T+3HlqJ4Dbnl2SkBt/tb1AlHQoxgqAX2UM8Hi7d+22c/sO67NLmy/C2DGPqBUAc0XaUC/i/RbNmrOPH5IKAgAyEmWXBz0pPUb0Hj2OzXkHDNnhKs/LooJCfaiw9Lmvv/66Cths+v3LLjvDZv6Ui5QaKQcPHLTkq6+rK6ss9x7PzjOXuJSoxiZ/jfCMqgl89823QgNovrzOPFl7lFv5Tt/effStwPodQb358vMvRIeQoZ1AVha1BKfy7MrvY7s2bf/evMXp9hKB+vb9/PPPwWHLmycvW05IhSMKeHInPfOU5SMBvryQOBAAkHmoHnDWrFlqB5UewjI+DZBrrhoHp1tmzXyxttOtX78+Pz9f7pTVHYcycfrfr2mAu0bdvunPv6JWLHEnLMTyoQ+tAxT4802J6Nhj1Syh8IMWbfuX5T/b7wXqzebNmxsVFQu7TcsC7vz3mmIkfhEtXsuu5fN4mzVp+vuq39JwtqKqqqpNq9bGvFLI7le+QDIyW3FRwd69e+Ufp89lQ9aAAID0JbYZuvq33/2hqOf1tcXD2157X9irsrIyag976sTJ7l27pflaf4TXk0elIg5PgymNFAwV789/4L77ZdIcaAi6SfHdN9+K1ug1t4Ybo3go0U/QHHHUKho+bKh20c7UVYZi7xNOlB7t0aWr+t4pOe14J0CJWqQYEILt159/0Rt/mvgCjR07Nnbn8OC99wUMf0PnrxayDAQApCmxfU5OHDvesX0HI3S94c1f17EhaKVJb8vly35S44HlvKPvvCsjYvwpn34jI1U4waThUSp+FhUUThg3fu/uPbVVL9QPWZmfLFwU1ABaUD/l4Ouol0huvt974MABnnhdsddYVVXVzdffoMfRwv8nzYvRGeZ5RO8nVHrUTCap/GpYzvXLL7/E3kQuGtuWTZudulrIbhAAkNYoi9ximt82YqTXzFpVv3j8wR1XoT0DI4ePCNTSq374/gd6ePX0LLotIlcA5KyznmWmedNmz06eUnr4CBPAicXSbOa9N1fKLWMbYhpsDpa+BDOnz7BfLcRPsOqqa6ZMmqw91vC3jxWANC7heZACf76e5DEdHOsrKyubNm4S+xbEYOfgFUIWgwCATEJ2gsIu10N8msF86jYwyEnx4sKi3Tt3RQ14/9+/WxsXN/JFRs1Pn1L7VUXsiBC38PxzU08cO57yZ+VSXnpxlh4r1vHwoNINrHvXbrWtcUGcfP/td7q3hl0GUNKyhKeHZF5zuQ6gSP3eAP07KF4I+z72LYgx6N+//0nlFYJLQABAumNx1hT2etPGTfS0R/VLwCQXEIRxHDVPZFVV1cX9B8QZUMip4s2x+pbkGzvJgq+F6T/5mUlHjx49b91C/YhuTFfXTBg3XuUJTgcBILcYrv7tdycqKUvYtWNnSYuWhkGmrblh+qd9CX/7ZGxQYx1Aw0EXIMH8ufNijzKW/PR02pAoEACQSVRXVw+86uqEzLCKg7Rp1brs1Gl5ZEuvOmPadHODQZru8LOb/kIPyNeFPv/jjz52cP8BdV+MGEnFulu9qnrY0Ft94e2hToYHVfHRHxv7qP1SITayuioqKq647HKfMddgzaWNBkjvYqwA6Bni7RogkMKvhmXBWWjLGF6mcntbvs+/Y9v2FF8nZD0IAMgk9LifKh5//aZXjRxJISyrwH+u/0Pu5lQHT9t1AD0OiTRE7hh1+/b/tlnuK+ptQkOIsZVQvDhzumxAv/5yP4CMS+hUC1F7wVs0a15ZWZnyespgVLq38U+M84biycio//aIq5S0LWojfujXXLklJhwXyNGmpZRAz+49ar+FXNnkHn7wIaeuFrIVBABkALK73LJlS2FhoUrfqwe7rMfA0KdXb5X9SjfmysvL+/bu4zVHC2PMcHoYi1GUFTLwiiv/WLO2ttoL2JypAiiBZHJg3/52bdoGNYCjCcLM5BVBS2jZsmVO10rGIL8Y4gvy5edfSCNSBnWN+u1DCaRt0RS4mfXFjAvUrEnT9WvXpbpdRQs/+tADD9Z+C8YAl+/zHz582P5ZgHqDAIB0R/Z3VRWVlwy4uN6GlJ73V8Zl/+abbwKR3bH8OeXpZ8yPpEWEb68ZylolHrKG+c/zdOrQ8dNPPw0wNqQH6imsWrEy3+/VNWrqm5OpXYPX8NjYR9Mk/HmmsP2/baEgLfqMg5O9QdTnq7JQqxwgRvaJ8BpmuETVKq4UMMHaKGnR0hJkM/WIL+P777+vZ/LWVaXp6Rp8mtNfmIY3JyQQBABkBjK4Sv0GYG2fgBEh58rLr4h6ltW//V7g9aWfj6+RsEyXMVISFBUUzpw+o7y8HJMu3Qg+kZrA7Ndel2aZvVGlpl15tfbfuqQV7SQGlsoRXysZCUDWXoHXU49ww8kucru5nn1CaQCVE1cvjl9w2pRQFXm8Hdt32LdvX9RmkIyQWVEV+D///KO2KPijB5gKPtZ2bdpWVeDFBwkDAQBpit5Fbtm0ucCfrzbh1bVokYKMKfOVv66wn6ii/FyfXr39to2/Ti8CRMzbyeBFcs5v2NBbd+/cpae2wbxLO6prbr1lqDS87BZ/CjSA12jDeTLxxdq1UZzEQBLx9akJPDXxSa8h1cLZnR3tCqI/X5+Z/UNfG1TWv+Wn4xecJsXIlxLapSO6fUug5NR0pOos1dXVrZq3kJMCtcw9hdauc/M+/2xxCi4MXAICANIa0UVWVlZedsmlPk+EK0X8xTTfzYmxPM+N199gJFeP7OVfnDHTq/W89jVZh4oxfyxjH0nrv2vnLj/88APmfjqjns6hQ4dKWrS0O4tL4yPpho7Rno3G/8LU5wMIRRv2jTE//bhMizZmuI+n4QpAyM4P55yW16kmO1gBqLXeQisk8rX4Vg66euC5c+eitofkNTb9LDdcd31tsYDUCp54ygOvujp5FwZuAwEA6YvsH+e88abX8GSob7x/swMVw7ko60I7ZS3e/9v/21ZcWCTGSzX977Tdr4rR9RshZTzepyY+ebbsTG3VBemA5Vl88cUX3rAtbrj2qlwWSS2GzPB55NaXi/sPsF8eWCgtLW3buo1Xmz5I8z0A8inrcwRqySIyWEJ4L4rTC5sOFxWbS34fRb3dMer2VIZNsxxz0tPPWNLO6DNQxnP0+sR1btiwIeEXA+4EAQBpzb49exsXN5LWf/1m4LyRr4PT/9G4ZvAQFS/FH60Xdm6sMtyWxLg+oF//P9f/ESCfayZgeUC33zbKlr8iFQJAnkVFPhHl6JFSp+okDbH7ZFdXB9M4eMNzB8bX0OmEbrU83JCuk0a/LxQs8vprr2vauImmBKIXlwsAGdTVb1rY8rv53JRnYzSMpLLw4wX256WtSAevVsq8CePGp+aSIOtBAEBac8tNN2tz//VZwtYSBgd7zzW/rw7YuvUP3/9AbfXTJ/71ORinBiq5VF2YXzBj2vSocdxRAmlF1MTSgVBU0EZFxfoiQGpUpWz2yv1DGBlfLP7csdpJP+xBct99+x2vObMu3etT46xVvyLtQnmFwSgxIYSG+e+//xYtWPjY2Ef79b0omNXEvCOX2/16Mbd2hD2mRBGGeOrbnmDTho32xPNSxYVeG+vAorRqWUJCD0gICABIXxYvXiwH4IYIAH0ab8igwYb3v3aW48ePty5ppQSANjykg/9PcKn6or69xfBw3lTwKIE0obZMCzOnz9A3maSsjakVAHm6Rx8Z60StZAb/bPlbiG0Ze1c50Bved+nRIVj7h1CWElFGDh8RqI7iwSJ+njx5cun3Pzw7ecrVV15V6PPbNUB63lryS3CPhPQFUr5S4un/tnJVClqapbuuPFchdJov0jVLkwRGOj/5jj2NMUA9QABAmnL21OlO7do3fJRSs3fi508//WQcXet7xz3+hL5PIAUDj76kYBmM9SSjwcv2+iZPnix3p2HfZzpnz57t3LGT7vaTsihA+q+9LriQaOJRqays7H9RP08aZP+wF+XSI+WctFzFC+m+0vvCXqdPn47nHstOnf7hu++fnDCxT6/ekW3DklrR2PQsz6s2IIV/Zm8+gTatWu/ZtTvZjc1Oj27djaFK264dtUrvGnU7X2FoOAgASFMmP/V0QnpzNciJoV0dXBnTf2/eku/zW2ZZkl1q216sx/oUpWP7Dit++dVywZDRvD3nLZ+ZYC5leQAsvwpj7ljpUfu10cZemPp82nr7iGaTl5MrjW8V5yfYZXm8zZs22/7ftjrdqUxScWDf/rnvvjdy+Iigc5rHiBqk1j1U5ChdA3gjpqWzs4g7vWTAxTK5Siq/FDdcd71FAFicUdWL4vyCk8dPWB8oQB1BAIDz2IMtbNmypcifn0ALSQxgMoKypaMcMmiwnNNK2QqAPv2vDEF5dik/5Bh/24iR0kqjZ88mys+cbdu6jc/M7pmC9qbvOvCbAmDp9z8EtK3ktDHBX3/8KX3lpbGb7OdS16Jv6jVM85z/kxsAvv/2u0DI779O96v/vTB2ly39ceyYR1qXtJK3H571z82Rp1NOMqb8cL5OklHkiooo9959j6qf1HxBRP3LpRWpxCyrxJZZg3nvzQ0ErDuV+SJDnUAAQLoQ7suqawYPHOSPzJbakEV5MZJ17dylurLK4pwtJIFm/XtS6QKkbkrL924EKi3w57/79jt6nUSpIshYXn35FXOaOUXtTRkQPtOInPrsc1GvzYVNqyZ01zIJoPw+Sh+bFDyXOhU1726JTDplypQGPjW9VxGq4LeVq8Y/Ma5Du/Yebfu4mc1aT6nufJ0kqaivyZtvvpmYRhYfr7z0sloB0AWAfmHqzeuuudb+EAHqBAIAnMfSeX266BOfN88y4dGQ1QDRq/7v9dmWc4khv1OHjmpSTWYLTkGmT0u3rm0GDY6yXTp1Xr92naVOansNmcjxo8eKCgpVpPaUmTUFXp/R2DxePRguLSoQisKeb7pmyb2hKXsu8XZiZp4vfTFn0NUDZUAYS1aTeG45diyBmqrqVStWjnno4RbNmiu/f7vzT/blE1BGtrjTAn++TBqTGr5Y/LlcetL3AKikNH6L76jXV3r4SNRnBxAnCABwGIt1W37mbMcO7fQBuOEDTJNGjU+ePBmIXCp9/dXX9ExMTm0CVkVczE033Kics6OO5fTvmYv+7EbfeVcqVwCMSOc54W2F7Vq1tl+Va1m3bp0v7JWXU79oY0l/iN7w7Ht+yD+ndUmrg/sPNLCLiPr3eudTea7im6+XDBt6q9wrZZcBWSYA/JH+cp06dExN3gxR20Jx2QWA3+b8o/TJRx98GKij6gPQQQBAWqD6r+kvTJNzXarLa2AYfvFxmTlF7yJPnDjRonnTUAyNPG/O/4UmR1Mx/e+3+v2Hx5spkyZXV1bZ6wQNkH18/+13IceznJStANi3He/bty9AcwoEzpad6dm9hzkXkL4Zf/X4PHJKfvkyI6ZZvR+i/YMxtr0eOnTolZdevqBHT+kalH12vyyRASFy5byMpWdOElv/+VcKUUsUILU2ZRk4ht58C3sAoCEgAMB5VLcljJJGRcVyrkvv+Lw5ufn1jZoiPvjfv1stZ3z66af1wV6Lf5eKMcZyI40LixYv+sRSG9j9WUx5eXnj4kYp3gNgKT/88IO8GJc3rUlPP2Nxdk/XTa7KBSgY+P/556Y25MGdx/8nRudTXbPil19vGzEyW/MJaEtzxqYs8Y5QPvWu6vg5VnpUyg8Z49WvOYhahj8pA4oLi86ePZuCC4NsBQEA6YIYZu4Zfbe//mHmcrUSXqq2bJYK1AT27NpdVFCY7IHEks9F9eYWX972bdutX7/eoSoHZxBNfdjQW52NpTj7tdfdZvrL2Jd6APW//vor3QxZteBpMfiUPvGGEhoarv+O1WVwskaIkNYlrcw90+HVCZVE2bRfrSsqCdnZlcrHUeTPX7s2uBmgHnst4kccsMCIfRePE1rwb3747nu3fYshgSAAwHlkF/bHH3+ojF31FgBqsPGaEQ8XL46I/llTVf3g/Q84anvlqvWNPr167929J5AVs7BZcAvJRq8iuQXFQctGfAscrIrUY3eTEDb0xf0HpJsrS2RkMG1Pqmn9l7RoeXD/AecqMoJzZ8vnz53X64IL9bwBKlOYTFkgvSvT39aP/VC6dekqU60ltaNr2bxF6IzGNvSYjTPYHp547HH943TCUCcQAJAuDBkyRJlE9YrDHQ5U5zPzVrYuaVVRUSGPLzvHndt3FPjzU2B7qb7bEohd6ZNrh1wjtyZnDQw/MbBUzprfVzvrSH3l5VcEkjyjmW5Y4g3Mfu31qF/YNCm6DJD9hpzR+O6bbx2sw6hUV1aJqxp09UDL5uBgVgUtY3Ft95j+RQ4o94y+O5Dkr0mXTp2NZ32+FYCgJ5gnt3vXbvKDKc5ZBtkBAgDSgu+++84bYSvXwwc37Mqvpv8nPzMpwripCdx1x50pm3mVs3eeXOvsl7gAcRmVlZXZt3kr++4o4ciaKSsrc3YFQGhjp2vCSfbs2t24uJEeWSV9BIBcBCjw+iydiWgwj419NH3UmtXorAms/HWFTK3oDTmyK7clOSljN/fTUADEcE/yebyLP/0sqfV5UZ++RlJI73lrJpScweMVLTl5lwTZDQIAnKeqqqr3hb2MnPP1D8cZxQ9Vbf+VA9W/f//jq+9m4roWfTUj30yrKe9RjeLgHuxP3Jztc6aIpnj65ClHqsJxxLO46YYbHaz8+hXRSZaXl+t34WAdKuyaf83vq2+47nqvtt9J9edpaPHHKJarFb8G/a8OHgwkrfKvuuLK0Lni3QMgRpMP5r8fcM0iHiQWBAA4jOizFnz0sTH37/FqY0ZdO+scywrA5ZdeFqiJGJ9G33lXvZyL6lP0nXx5OcGpGk8oweSzk6c4uXcvycjaZltzVCzDs7P7gEVT3LJps1NV4RTyEXyycJE5EZCG0X4iAh+rODAF/vxNGzYqOy/dTL3q6upA5IWt+OXXKy+/QouqaQ1/nGkl6MXkDQXflDeYjEcwZNBgea54BEBwjUU6JqVXW4CMAQEADlNRUdGlU2c5KogeTZZ6hOTXQnkGrW1hc7/79jvyFLKn3vrPv3JjgD+1E1Fy2BDWvygvz3opEG3kSLfhvIHMmjVr9+7gwnSW3VdDsIdWnDJpsoMCwOJN7p4ndfL4iTatWscda8WBEtUZ6aWXXnK65uqMaFTfLvmmT6/e3sgstpmoAeQGM7mQO3/uvCTV2OCBg0Kny9XDgNZSjKmuju07BKqDX16pwQDiBwEADvPWW2+Zs/K5asdYPWbmQmETwsGbC/MLThw7rs4ihqL7771POeGkcNgIhf4MlVdffkX21GGy1Ohav379VVddJSMVQm18MP99BwWAOPV777zrdB04wJMTJho2VmgbZVQby9kwoPZfB115VVWVkYsqs6SauFpx5e+8806bliWWdLbpLwMiIrGaA5O48qaNm+zZE4relujqqtMKgFrrZhsA1A8EADhJWVlZ+9Zt5IxXg6eIwrJBmPjDht6qj5T79uxVwX/k7rTkjRbGqn0oAobqo1OTSsZJIr2tOnXo+Pqrr9lHyMwyX5LKihUrnN0H/PxzU52ug6QQo439s+XvfJ9fhQtLQxcgb+RchvhVmJs7d+5MZQUmgxMnTjz11FNaBLZcw4vdSHql7xbIdfZ7EbXI61Rd+i033SzvyxJaqoG1JIYtWTlx7gGQ5dNFn9CvQj1AAIADqN7q5VkvyUS/slNTa9/1iMhh5vQ1Ztw/1XLrCiaMG2+kpzFT6iRwbFAZi/WLl0aGXHDIfus/YJ0Nmzh+QlFBoT0Hc8D1GkDPe53KxSh7GfPQw85WRTKIapAZL6prBl090COjrKQ2+Xf8xYyFkKMWM5PncJJixFPYtvW/a4dcIx1p5Jy61AAqO4pKJuD4g7A+F+065VDyycJF6r70Fw3p30YMGy6d04J6I2bnIBMsSE0iswG4vF+FeoAAgJSid1JnTpe1alli6dcasC4czgFcXFhUdiqctOXIkSONiorlHtxkCAB/5PKFqWeMdATTnn/Bfu9ZjNwI+Puq38S9X3HZ5coz1SW3Hz8y8aeDNo2axcxi9FYnLDZpuqnp/5C56Vj9Ry3mFYb2y3q8N91wYyC7vjviXhZ/+lm7Nm1lmCCZNUz1on4ZzuH8QTBTXTTf1GD3LkaT1iWtjpUeVTel32C9a+aawUPizwQcnGAKjWWWcBcAcYIAgJSiz5G88tLLXtusvz6VXscSnpu5bcTI8IR0dc30F6Yp7//QyJpgFyB1zfmRO4zFGR99ZGwgu8bvOKmpqpZj/BtvvBF+k1h1JrISOrRr76BNI3OBZRm1xckpO3XaMDrNviI9VwBU2B/RWTVr0vTAvv1Z9n2Rt3Py5EnRN8rQCD5rdCAjD25aFZXQIPxOnue+e+613FcDUZuA4xEAxmqJJ7fAn3/ubHmirgHcAwIAnEGMx61alshNeHYNUK8+OrSrL9Qvq3QtokOsKD/XuqSVnGfS3U8TOzzYRYs3J/fO2+9w4UZYNQiNHfOIqPAmjRrv27cvwOBkQ1SIGfbbmXJhzwucroOkEDUb3eRnJvnCWflypQdFGgoAv5nzS5QFH32cZd+aiEdTEwwV2rN7D5W2xez50zE6k36FoTHLWERasWKF5QYb8sgG9Osfdw2E27Bo2H+u/6NBDwZcCQIAnOHVl1/RJ1TUDFC983EaMT083gJ//snjJ9SJxCAqXU61Iyd46T8iWIQZ7vr6a68T2sPBGk4x9mFv6fc/yFX+EcOGZ2u8owYyauRtDto0QoHLy8gyKzNgu6Od23eIbsGj9TDenP8z7SfH6r+2zkRGK84yB63a9macOXNGbdAyM8EnJUhDA4s3Yq+X4T8mmlDvC3tVnqto+Nqm/Gz/i/rpmYDPGwZUTXt9MP/97PsWQ7JBAEDqUL1k+Zmz7dq0NSdRPMr0b1hgOCMz4g3XXa+bmxdf1NfcH5zjN/dOJXbAtq9g9Ovd59SpU/pduwd1v+IpNyoqDgbPzsv5dsk3eKnaefSRsQ7aNMImjjpZnm3UGELL/J7m+ky3EwcrP0Z/Iq6tWZOmB/cfcLriUsfPP//cuWMnOVOTnq5Zfj2msydXbgmQqbgsMR4a8lXq2L6DbKJSAsWcDpMxSfOk4+u4x59o8EMA14EAgBShd4vvvfOu6uijOv3XLwyo7I6D0c2rDaWxcuVKfaZEWv/1yDJ23oFB3IK6i3atWu/fvSd77ak6EApqEVRfnTq2P1t2xunLcRi7ZfDclGcdNGjEl6WysjKbTf8Qcj+6tus3N9LhJL1KfshP6f1587P2uYRuy5gM0t4+cez4PaPv9oS9a5x/FnqRnbyUKCp4UVAG5HkaFRVLL8eG07RxE3+8ayARkawGXT0waxsMJA0EAKSUYF6YisouHdqriD2J6qCDnbLMihJK0SJJjYuF8sUU11BcWLRu3ToHa9hB7Mlu58+fr+aoJj8zKeqfuQqLI8Ts2bMdtGnEQzlx4kSWP47qmssvvSwNDUq/6e3jMeOSGWZlbt61Q67J8odiQy0Of/TBh42LGynBJnO22Dfgpk+R68l33zW64Y+surJKJqqPM06djGgnL6B502a4WUJdQQBAqln48QLlh5NYASDGzj69eqsTHT58ON/nT/YAUOD1qfDVYrj6YvHnDtatg9itf/Fz//79Rri6PE9hfsGObdvdZtnE5sMPP3TQdhEP5eDBg07XQRIRje3TRZ/IwP8O1nPUoucIl32XjIfTqKhYfE2crrlUE+4Wqmu2/vPvRX36ir40bP2HqigNH2KoGFFB1/y+uoGVcPzoMTkgxiMAjPZjJicWHzxy6HBDHwO4DAQApJp+fS9SfjgJXwF4auKTajLp5VkvpSChjMpilpeTG5zk1ha4QXDxgH4eM6qJ2g3s2tqxBAlZvHixg7aLZbks+ygvL+/SqXN6CgC5MqbvR5KrAbNfe115MAZc0JNE3YVSfubsA/fd77UOEOmpAYyl7EsvvqSBOU/+2fK33Kcef656mZtMBo1d+euK858DQAMBACnll+U/697/CcyEKq3Mn39aLk8k+uKunbukJqOkHLmDC/dV1c5Wr+PYo+BNnz7dTL8QfNbLl/0UcLEA0BEV9d133zlou4h2u3v3bqerIVmI6n3tlVfTePI4V9uWYLj+XzLgYj1wsEs0QG289dZbhfkFen6ANIwOpOef+eiDDwMNeF6ib5RdpRQA8exRkWsFcgVg7rvvJbT6IftBAEBKueWmm8M+r6Fod4nqiMWhiguLzp0LRt4UXfCPPyw1Y0okvYgTderQ8eiRUpcP2FFZs2aNSvbpC/louTA3ggUlk1asWOGg7SIeyrZt25yujASjvoOnT55q3rSZx4z672A911LCkQnks8j3+Tf+tUG/C/vrbMXuQCjfWf3b7+3atJU1JhPfOv3Uoj9Hf2gUaN+23ZnTZfWugUULFuorAOcVAN5wfuLg2Z+cMDEBTwLcBAIAUsfWf/5VURRkxJ4EdsTiaNdfe13AHEvuGHW7zwjYnNwBQGYeWL92XaCW5WwXopbCBVVVVS2aNTejaAcf+ltvznHu0tII0UgcFwCbNm1yuhqSQ03g+eemRgb/cayeo/cb4RAuwRncvJzcpyY+GaiucZvpHxtRA/v27B3Qr783ZBanoZATJrh8lHIaftbMF+t9s8HcOPKL6YkrF7JX2y0gzh50sASoCwgASB2PPDxGX+JMoP+P7A1V53vi2PGigsLQ+0kXAOIuXnvlVXleBuyo5suokbfJZZ/gPuncvJbNW+iZ2tyG7iW1fv16B20X8ZXZsGGDs7WRDET1Hj1SGoqoGHLPCOX8crCeaynhsKRyCfH06dPq+i2340Qtpgh7j2GfRikrKxPWrYp4k1YllLHLaGbiV9HqSktL61cVMnW6P+5NwMGV1ZBUKPD6vDm5F/Xp27BHAa4DAQApQnSLxYVF+WaYs4SvAIjy+6rf5LnmvvueefCkC4Drr72upqo6uwfpOIleCTWBee/NDQfPDi0BPTt5SsqvLh3ZtGmTg7aLeBBbtmxxug6SwpMTJsr9kVpxrJ5rKUYiJ7kstuSrr52us7Qgii9Q6Gd1ZdXECePS8DmasiT4KINJD3Pzxo8fX797v+mGG5ULUGhh4TyqVc6qyL8UA2uTRo0T8QTARSAAICnYN4POmj7Dn5zkO1JUNCoqVs7lV1x2uRxiE+0zmmt2zTnSIbVVSYsDB1yUsLN+7NmzR9NjwdK4sCC7A1DGibMCQBgQmb4JOKrm3Ldnb1FBYWr2/9S7eLUXQ4cOTX3VpT/2pYDZs2d7zXgPZlyB3HTYHKznsizOL9izY2dt9xLjZi/o0VPenV9N8MdsP2pjlfr1WOnRJD0CyEoQAJBEVPdx7ty5tiWt/Gav3UAZEPXjgwYNkufauXOnx8gunMiBQQb7l3PYxrydJ/e7b5foy9Z0l1FRY5uevXLs2LFOX5djKHm8ceNGp0wWmYVKCIAMbbR261C9eOThMd5ICzsNxYCKf9CoqHjv3r0Z+hRSzycLFxX485WhHCrOCwA1JMmU8A/dd7+64BhPVv+vqorKfJ9f5oKIp91aslnLCvl7c+IX9BjashgEACQd0Xcs+Ohj1UvmJy7yj975TppkJJqdOX2GOTYkdmAIb7eSJ71n9N3qBp2r3cxg/BPjzHDs5sZHn9/lecHEva9atcpJq8XjzbJ1GFGlolGJpqWHF0tPAeA1f7768itOV1uaEn0/dHXN0u9/EKpJBpFTcz2OP1A1rnlzcov8+du3b6/tXqLe6X//bpV3lGcmlokdxU61amNOLbRc8OMPSxP+FOK8BchEEACQFCI6i+oa0ydHrduaJkjdlwLURyzH+eqrr0InDlzUp6/X6I4TKQBkZkp1urat2xw7dsx+y/SSdkSdfPP1ktB8Vch1KrQpU/x6x6jb7X/pyBWmEv0eHY8CdPLkSQerIlHoVTrmoYd1y0kloXOwnqP3J6ELu7DnBZXnKmpMHKzDtCWqDPj151+aNm6iG8FOPUf7eCTHuOD0UHUdHqjsIfWUKfpqQNQvry4A5K/vz5uf8GqXL06fPv3nn38m6uCQJiAAICno1vCmDRvDfo1aB1fvoubg/dqqwv79+8W5tmza7A13jonc/Ce3W6nedslXX0cN+skoHpXjR4/JmBVqBUA+oz/X/6H+xlVVJ2/2p59+cspw8YemGysqKpyuiQZh+ert27O3wJ/v0Wwyr+kd4WA9R+9PQvW/bOmPAZe1/Dg5r2vl+rXrmjVpmg7qTo5H+qgkv1xb//k3/vudNfNFOYuvRrTY7dbuAiTKizNmJqr+JarmV61a9fbbbyf24OA4CABIOo+NfdQyT6NkQD1mbuxbCMRBOrfvEDxTdc20518Q58rLyU2KADBzdt5+2yj7mMQ0XixqAv36XiQDAamgGWJ4C6Zu0ObJ9AQC2YreQpYsWeKg4VJcWORgPSSKsI1YEwykqOZNLbaRg/UcvT/J84wcPsJ+I2AhRrUoDeC4wLMMSVJ53nXHnXpih9puRL5/24iR+igpB8d4XID8ploQZcK4egYginFh8ufs2bPvvfdeUrhnGQgASDCWbu70yVNyrdbeYzZk3VbXEqKMvHVY6NyBvr37SIdIXyjtVEJ9Qw050bi40b59++KvAZA88djjMvyr3EIt3YHEyL3y1xXyD1xYbwsWLHDQZGnTqrXTFZAAVLM5dOCg2h5q2E85uQ46h8QujYqKd+/cZb8LkFjWdmpbEFAawNmnqTczFZZHjEE7tm3Xrz/GzXbt2MlywNhDpLT41R94lORIDvfefU+fXr0RAFkGAgCSgurv5s+dF2N6pn4rAJYXokyf+rw4l+htvVrnWOD1JFAAqHSPr7z0cvAGjfu03m/UX0Hw5edfyBWACEcgT+6QQYPdU12WO507d66DVku3Ll2dqofEImv1yQkTvZrTf0OWGVNQpr8wTfYe7mn8dSWuXVU1gbWr1xQXFjn4KNWcve6VKvO7PfTAgzFuTb0+evRo1CPH8JW1rGvJX2+8/oaEPoHwdQrr3+/1nS07k9jjg7MgACApGB1HTaD/Rf1Mgy9hHW6wq/XKzLLGeL9kyZKAmUpdjf2xoyict0O3/Rp0K+rZvce5c+fY71snZEUdPVJqPp0clZlVLtes+OXXgJn0x1UIMemU1SIexGWXXOp0BSSMwwcPFRcVpEM0GHtnJYWukf0w1Bl279ypvLzcfe09wagKXLZsWVFo74fXzDKpJVp26tEHm2KBP3/Pnj32C7bw04/LGtbAjNeX9OufjHo+c7pMWP+iYjf89Yflv2jDGQ0CABKM7vK4fu06ma0w4QOzOZQaAmDXrl1CbAweOEgtjEovzBhRFGJ3qXrEUj3TyrKlP+pdnht81hNGyEFL7c1Q45Z4gtcMHmL+SZZjkY6Tnn7GKQNFPIibbrjR0cpIDLIyn5vyrNqlk34lJABETxLanyR+frPkK9YM64d98kW+/vyzxTKOvuphvDn/56ggDHZx4nomjBtvf9aWu5g188X6nUVfcxA/e3btlvCqFqz+7XeprBYu+CiBxwfHQQBAUpB9xwP33S+HvYR3xKYnSdB8lCnQT588JQOA+MwoQ4na/KeONmTQ4Ow3UZOD3KP56CNjjfT1mqEmY1+sWbPG6WtMKdIIGPPQw04ZKOJBPKClK8poyk6dbtGseTImGhLVU6nQZ6K1Dx44yO46iACIkxirr+++/Y6a9JEbjRwXAOJ6igoKjx07VtuDVjuA630ifb26dYuWyajw/70+W47jz06ZVNsTgUwEAQDJQozKwjSXMwfJEwDi+JcMuFicbslXXyufHzmFXz8BYHEaVm7E4uDr164LxBGfDuzIilq0YKEWDFQNkzmiboeHtnG7pz7lnYq7dspAEV+NSU8/43Q1JIa33pwTzAmV56zBd56eSlX7xr82uKedJ4+ou4SnTJost5x5g+m0clXmFidKONjxjGnTo1658aK6pqRFy3qfSB+tivz5iWpa+nHuGHW7N7TqPnzY0IQcHNIEBAAkiwUffWzOxyR+D4ByARLHv//e+8TpHn1krMXir/ceAHuYUfHOiGHD65TYBezIXdrKUJPu0cFYQHlBDbB54yanLzCliCH28ksvc8pAyZoctFUVlV07d0nSSmMC6tkIfWsEJsqaVRcHUZMvERpA/qyqHn3nXUY3bsw1OPPc5U4nOQ/VplXrc2fLa7sdmbsmUec9d+5cwiu8U4eO/tD3q0f3rvaah8wFAQDJ4prBQ9Smz4T75uorALNmvihO16VTZ58WQVklU6xX3x0l0NDGjRv1u6MHrB+tS1rpLkDmMn3waQZj2GV1pdrbjBxZnTFQPN5PFi6yXF4mtuovFn/uNa09R2d8z99TNS5udOjAwQCeP4nD3mjLz5y98vIrHE8OIMxlNfBFzdGrLnvOG282MDOmXo4fP57Y6j148KDXWFTPyfd7VQwMyAIQAJAU9uzZ4ztfLsMGDqtysBen+PrLr/7991+/seybsLTwKr+j6PtujhZejX4wTvRBetjQW+VMrRnE2gjWEVQFHq8eGT3rqa6uLvDnO2WgiC/m76t+y/g2XB1cRfFokdedqs/aS7B5y85w5vQZTtdXNhDD/VK+eejQoY7tOzibHEBOc5hbETx9e/ep7YJHDBuekDNKFXHgwIHE1rYQ2B4jJEZw+9aOHTsstQ2ZCwIAksKsmS8aGXmSMyTLYBr+0Ck2bdj45ptvJingt+xVf1n2UyBa6AmoK6++/Ip0ATKHZxWtL6jonnjscacvMHXs27fPQRtFnFpmKZJkYnsW1/zbylVq2096ZvxVvuCdOnQ8e/Zs1LtIfdVlDfYVAPnrn+v/KC4sSuDMep2/Xx6jZxN2s8x4+OOPPwYi1Yu8+JbNWzTkRJZRb/fu3Ymt4WeeetqcxQuuZS1fvjxAo80WEACQBGoCPbv3CE3x5ikHjwQOqOHDhob8k8dPJGoSxei7tTCjovTre5HTFWolc/vfVavCFpte51K/NSkqPn70mP1TmXu/FvQbWbVipYMGqxjULX7J6VnJtV2VjCt124iRMue37BMc9PnWm7H+q+qpFi5cmOKqczkLP16gQoLaStJbgi5H5ethQ2+1XKFow+vWrE3sIvk///yTyEqsrgn6UwXDWBmhsT+Y/766eP1GEnlSSBUIAKgnMdxY1/y+Wt/+m+hwbOGZFXGWls1bCDugVcuSBPfdWuwO1eU5hazhkydPBjK2q1WXXVZWZrF69R0Xwnh6ccZM8UCze3SRd/TRBx86KADatWmb6Zva9+/dl+/zeyNiPjq46TMiEazahiSvakC//jJnSPY15nTmsbGPSmdRc84oIgthUotcAJdq0Gv6p+3asdNyhVOffc4XbioJKJs2bUpgGys7ddrv9cnakxf5/HNTE3VwcBwEADSIKDKgukYF5JEdh/KDTNBAm6OOKbrOSwZcvPWffxNoSMnsLTLTsC+UZEB0go7Vr1axamon42wIywX36dVbzZJaXogKb9OqdUV54gNZpCFiKHVQAFx95VUWoZVh1BgV6DFn2ZWR52BREQjET5VBXBigy0M+hJAyZMMuLy+/9OJLZDg4LThEKoSilpHAK1+Ly5gyaXIgsj/s1/eixHYClmAVDUS0W7WKImvvntF3R61qyEQQAJBIRF9QVVHZsnkLT8iMVr46Cexwjbn/nP+TjpUjhg3/YP77CdxpIE0KI3ifx/vg/Q843sHJC1i3LpyFIIOw79i7/977LHOllvr/+ENX5JsUQ6mDAkAGz80Ioi4HiX6mVcsS07bL0RYbHatSvVWrF6Ibuf7a67I7vFVaYZmT2rVjZ/Omzcx4dOE1mWQ3Bj0pjd9cBChp0VJoEnl51dXV+/bs1bZCJea8mzYlLJiyqEChseVKfiiiUfBGBl51daKOD46DAIAEs2zpj3Lvr5w5SMLSvKEogm76eZ7HH31M2OgJdKM0x4lcGZz+91W/OV2jBn/++f/Zuw7/po0v/o80iWVlJ0AS9l4ts2zKhpZZVltmyygte7VllgAto4PVQqCsX8tqgbL3HmWXTZMQSEKm7d9JTzqfJduxZVknOff93Mc4xpbunu7evffujUu0u6AdpAy3dvUasl4b76kDIIGpbes2kSowkXTo3LFTuKUQP23JkiUU6RA6tm7JAqsqxACEwdVQY4v19OhAPbx8UVi5llPdIwBA853bd8j7kTvzWLingZQDg/ACgulKWjd+WLOWdweK6HNffU8AunV9T3ECUKdWbTWFGSwKpgAwhAT1+v9s/KfA7HCZJ91jAARrBBcDCsC3S5Y2a9JUR0sqTk+JuHW9OnXNkxz9ypUrLotXIAZinjl12iaL/rzqHAA8u04eP0H+imKf9YJ6FNXTM8Ithfhp27Zt89pP8wMo2aVTZ9KsSz0GgJzMPKEGfDRipNc0NQzhAARauFREHjt6DBGVa9AJAHZrxDsL52lBBxMAJEHW6746KgBFhW/iY+NAwcYnAHF8bHl5uV63YKALpgAw6ImSkpK0qtUUrFZv31wprZCQCdTGrf5+Fbbu6NJk47TAlKd9OZU2Rd149OgR7S5oB3ku/+bNG56zkwoAaTEFz66Rw0dEpJyE9bfs7Gy6OSuRPmkVCqtdgC5fvgz2VDnQSLfqH5obmf8HS/8JfOy9e/dcFtfbIwBvCgqbNGqMC1oZpijaCTUAc7lbN4VoLiILsJ7Zq3R0Afr70GFYZVhlghWHeJdet2CgC6YAMIQKcm/bt28fJ5s6YmXfwXC4AEGaTnSXeXPm6pv/Wz4pFk4YTp48SZu6EhB5dU/wbDBI6eed5m/zKtGfJ9Imxtp5tEG6IkhmUgzkyOG/beGpXBFgQ2qYr76ZE2QnJ0+eDJVAINt6rK55VLQ1sg/4zVSxroUlyBsB8H/SgktGGBMrgutIkvMB2qwZM1F/MjMzsbEJatro0nQ7AXC65syajXsIOzhstdevX2dTOjLAFAAG/eBwjhk12oC9lsjnYBs/eoze1xcL08bYqqdnOMron3U6CaunpdkueQgwatQoP/SHFtn55lZ99z0Ir+FfL9GgzZJWxkYNGtImgHYUFb5JS61iAJ8Jls4C6+Bi5PqvMcmJSc+fP6dNLQYBwHxmTp8BZci9rjvDdMgaaenlxSVvN2mq132xsoHajRs38JAD3C98fa1t6zaELc+GTXuHDx7S67kw0AVTABi0Q8E4ykvLUpNTws09yUNV9KZH1/f0tv9JhXtGfzKKFmHVKC8v37lzJ+1e6ACYM5mZmb53MimdS60aNSPP2RQvmbGjx0BF5HCvFwjCIetaeK1JZFqonWd+27ot3ETTzjdE10TgTpDzkYEuyJlTXFzcvGkzvO7IfYSLijbyOG7q51PIPzXvX+q9Dw6KAzcVea3ng97nZufgwGXoIbjaooYWoPonDFYEUwAYdMP+vfuMcWu2y9kV0Os7TZvpa7mB4040kE0bNuKhUed0N27c2LVrF90+hAiShn/++adi9yKDgMFojR7Bnt//oNhh3UHutW1bt5GNkeFeL1LQPKkAzJ87jy4ptAEI2K3rewYwGW10htzB6M/kxCQkQtEmGIMSsiMQFAUjyzVIO0u454mC49mJTEEamtrxDCkAfmp0Vgj8k107doLHPy6hAPoAel27eo2ej4SBHpgCwKAR6sg8yLRgwF5LcsyMqtX0vTiELKOB3Ltzlx51lVj/87rDh4VaQpERTfjkyRPFAyX2xRisg/Xt3cdl/cECyFE4ysqFErY2I3yRJWJ6ngBsy9pKkRTBgiTd/bv36MZO+G7RkMBKzE8W/fX8ryhSjMEnnK6Jn02wqaRw4ycMzn6m+QTb669ycoJTO0ltgdxcPPMmSXwDVIKF3yxwRQpbruRgCgCDPkAyTbUqVQ1LbAJMM46z+2GFmjdyJCrVrV2HNkXdEJzmP/7k8sVLkcRz01KrkFsgSX/B/G+LhhTvD+7dp91TnYEe4j83bgqSolExADhoHj5BhL19+zZtMmjEvDlz7SYI+fVBZ4m8iBO+evWKNqkYvON13qua1WvgjYOMKDOy6XVHxQlGfn4+DFObFxC8dzgc1dMzOCKZKS8HAKDVN2Xy515/zmA5MAWAQSMU54wnj5/gDDSlkDfS+6bRvN02dMiHVKnrgbKysqqpVf69/wD+tBbD9WVhguANb49S8qUGY+pX8+a7rDZkX5BG4XT9snGTUCrbkBgA7JoCCgAiaUpSMppRtIkRHCS5pKy8ds1appT+3bZSNG8XLVpEm2AMbqi5x9atW+2eefq9ViUPR5MKA0dJqavwq17XhwHiegjacOHCBc6dEVvqpF32CBr18SehXJzBPGAKAENIwCLd9KnTDPP/8fOnHk0wl367ZKkrmEQKYcWRw38j2ha/KTJDZ0IHjGLq51N8PDtJAYDpVKdWbTPkYtILwtidrk/HjbcbVZFU0qbkGABE1c4dO9EmQxAg5zxaCOaU/nlCAUirWo2Z/80GhcMqQvcuXdVpiK3eUhOTvI462NOABQsWqNUSUANs0TGQQiAynFErOZgCwKAPGjVoaDPW/B823i0ITAf27TcPa5v42YSMtHSXI6IY7k9r1pIPlHimksM6lqgO/vkX7c7qCqfr7WbNyfI6YW5SahqsAHzx+RSLzqJxY8Yaw2E0NDxdly/LpE0nhopx/fIV0oPUeC8gfQ3/0GqmZ7hCFs3RD9u1a8f72GHRPO/+Xjc9nwQDPTAFgCEkAJe5duWqcDho07OiIcWGhDOoQkWXqvBaVPgmNTnl3TZtXZL/SITg70OHiSNm8IeRNhjFfjN86DBy2BYVXgGo869e5sFiAau8epfVOauVVCdbSm6L2pZfN9MmQxDAj7uoqKhKajJOt0+dS0AjZDhBy0KKekFBAV2KMQSISRMmwoMj0mRZZv8CB33FJ82aNNVAB8VBwfOnz/wc5iPu1K7tu2rHTgYrgikADKECrf9vlyzFMg11zhh6S4iLNw9T27olCzH6DwcPiTAF4NGjR5w7CE9yUAE3U9Iah/6Mj417mZNL/taiuw70+c/9B2T/2micjpAPm/8xvheUt0Nz6e7tO5ajHurwrh075bxJNA0NpLXY81U4Y8n8dhltUjEEihfPnqckJYtLLyYm+i3LKQCkmA6cs0O79sESQX1c8POPP/n35m3etBl8M8RIAwbqYAoAg3ZgroH4TiSdAGAGR5ew8Aq0FarHOywmtPkHGl1ifAIvF//CtnDOUxQGmfWnH35UyKwW9UBFHf56/ld480aSR7jjDrECIByzRMdkpKVbi2i4t4MHDpJFNMp8RuG8IcVH2qKrVknJf/Xaa+cZzIlFCxZKa8QWbUxaXh3XNX6PK/W+37df6DTp1aOnfwXAVCnyGEIBUwAYQgLa4XJycuzSdhgJCgBippB+nhwjLfKePnkKJOCszVto9SF8aNOqNU/4/ZObGe9Zf75Th44u1VG15aQr6PDHIz/iVJ5O4fM/Jk8b0Drt//4HlqMbQm5ubhwfCwoAHAJQZxSKR4b69s1XX0NvLTo/KxXg6RTmF9SqUVMudxVlOQWAPDxE28RHI0aGSJBXL/PiY+P8KwDVqlTV7TEwUAVTABg0Am9vWVlZsjNiJCgAqE349DP1MA0jqfvVKVg9gddfvnjJ4M4YgBHDhtvFIgC2GMEHVy0E440NUSACCgLA4ysrK1u8cBHaZWXv/LALCuKhSgzEVeP0VrSJERw2bdhoF+eJ7DdFn88oEjimJCWTjmqWo3BlA35Aa1evgfT2JplXGhpO0PnF51NCJMuWXzerzROKlpqcoqAhg0XBFACGkIBYwMcjP+KldAoRogDMmzPXpSmHWuggvSqvXr4imXlsXFFRhOQAxUDDWfD1NzbZfQLHAPiq8ICEZquHApMK3t3bd3p276HYaHFZUB39guRqPkKYNXp/7MhRK5Lu/b790Fhiot8So4zoC2rY8oqnqOCkJ4IK32DQBvSMSkpK6tWpKxexser+BVL7kkWLQyEFYrCDBgz0rwAgKiFdl03yyABTABhCAhJYM9LSbVJgXFRkKABLFy/BAzTsNF+RqRq9Duw/ABhxsyZNI5LP/vrrr1DuVxE94jUoVkhwAWHQKkJZFGjtZG3eUr1aGlkSiKSAXpIB2M4RheP4WKRJuqxGt9d5rxLi4nnZURuNBekz1LkEmbs2OT7h+fPnLhVhrUXnSgh4POt/XgcMB+cis1DDoVOIl25cvyEUOuTlvkQsIraik0lOLjfGYHUwBYAhJJw/f97mll0i5ARg7eo1VHZu8qZnzpzBid7GjRnr9TtWx/Hjx2GM6vBxLFqRhegvXbgYAcNXDCEvO2f0Rx+rdQC9GifHONq5mK6du9AadSjYuiVLGousAJjHVxue1xeTJrsiSDWtPICHVFpc0qBOXUGjs1oWO8w3wE9yz+9/hEKHXzf9gpmGn5uie7nYDI8IMAWAISQs/GaBIMPFRIjtH3jf5l9+ddFjcHDfju07QOwmev1l4yb1FyIADx8+VCTE9Hv0HDVz+gzFFaxFCj8C4oEDB+rUqg2RiO5ayJ5Nm3+C7Dcv0Pbrr782fNBBwJf5HNwSbLJOWKGPcrgbeWaFZK84Pvbx48c0CMagG9asWi2HAlujPDDY6cnTMPTnuTNnQyFCrx490dUqDLJHN7IW42XwBaYAMGgEKadGpALgoidfZm3egiVj9Hrn1u2IzCtSXl4upHYRh1mhAoC+UK9OXetSQP0EFe/zX70eN2asIIKIbu7ympLSd4IcryE2AJIbgrhw5MgRl1lnka9eFeYXJCUkymGa0go1rOi414ZdqoTOREWTB3QMFgK5JIvfFKVXS1Nk1g9zyXkdtiqcuhR1Ei2Khw/+1UyH7OxsnrNjA4Sf+8IJgMusnIQhcDAFgEE7Xubkxtp5ODkF70nT8sqguCqUSjWYu+HbvXr1qmb1GriAa0ZaeuRU/1KhUYOGWAGAg2xfzwXqWJ07d866upD/UgYQhHfor4N1a9eBgF2FOKIt+BWfACBdq7CwkMKwg4FaQRLqf8mZoIC9wHuKLAKc1sBdBPXknxs36RGMQQfA0oNyluoiD9S3JB/8MAYUAOCcgi+QjSspKtZMgdXfr4J0YYErAAxWB1MAGLRj7x97pDQ1nPW8J301NKIN69a76CXz/mz8pyDxgMw3fOgwl0JMjBQ4HI4+vXoHfgLAEblWrA4/7kBIAxw7dqwk8oo+vlwICgDYCNF1evXo6aI3qwOEUwb+ZPQnozAdyLSwFPmDTTZ5oJcBH/SnSC4GHZGbnZMYnwBzjAzHD3epvpCmInH2Xj09Q8Oo8Vpr3bIVKAD+XYDsYtoGxSJlsCiYAsCgHV98PkWq1hT1lhXzJ/jiqmtXr6FF0mNHjmLbPxhjNm3aVPHPLItPx40nHN8rUABiogQvIKtXRK4wVBQ21z2//wH5tYhyAVoqlUqBs9Exy5dl+rmpOVFWVgZEIDIN2DiqMQDuZ8EJesjRv49YiJ4MapAq8aQJE6lvQEFNRRyLghZIh3btNVPg2pWrdogklt0O/d+XzfnIAFMAGLTj7WbNgSNAmaFw5DChwlVXf7+KCj3z8/MbN2wkJzmRvMCfPXvmMr3hVhvQcL756mswO8GW49e3W/IRunLpMnkFiv0PFuQxjn93IPEjV/aL/wYPHMR5VvMNej6LziqIbmiPt8Q5Etm306dPe7X301UARO9wYW2+26YtRUIxhA7FQrh9+7b6iZtzU8MuQOIRn2AmGDJosGY6zJw+Q4qB5ioOAmYuQBEDpgAwaAQSTIlq5BGSAJQXmaBQc4pGUr+xo8cQcp5A0o4d2plZVgsd639eR5w7V3ACADvx/Lnz4LfWokxJScn9+/cD6bPiOxvXb0hOTBJ9YCrYmL3PZzFvZvX0DGe5w0JqJHRy7ty5vEoCoyuQcVKFCoGqO37bTptODBrhaxX07dmLjP01p/SPpyKpAEz94kttpCgrKUX8gZd9iuQwAO83FXyi7Lx2ujOYCUwBYNCI37ZuUysAZmaXgXPVz8Ws3gZj586dpKEXTIzLM7/FX7CE3BYU0IgO7NtP2rb9nABgDfOd5m/T7rgWFBUVNW/evEuXLnfu3FH8VwVyudN1787dDu3aaz4BQO2Tjz4m72KJuYQ62aJFCzwQhXM2Rf4gdiamfr06ZWVltInEoDP27P6fYsqZs8XKHjtCC81zddeOnXhug73AP5+pkpKqL80ZaIEpAAwaMX70GGAH5JZMfXvWpY0cPiJ8iXfgwgpnjHv37lVNTgH+K5rDYyBv47//aknrZiFcuXQZu7j4T+8oJ8IXNjxsSreEIAu4ce06jDE5MWnrlixfE0zhHYTfI1lz4TcLIB5AvcpkqdQjewm45IGrwLZt29TXNDkeP3xEN9uPjyZISGiifvfdd9BPq9CTIRCghVa7Zi3Mc0C8pj3lvPFDeTbi6Kl9e/YGNVJp3jqcvXr0JGx5knORr/siriIkpmOICDAFgCFQKESuRvXqY0HEzJYSDa37e92QfBbufR2LeoWFhS1atIBjXAgAAHoK3Yh0ZL/4j5T+/Ww8sl1K2JLXrFptCamL7OSMadMhzA6E+GlfTi0vL/f1fa/qDfrz7NmzjRs2IqNgvarcytQlnP3ly5dhHquegIFvXL/BhLIXpFSqmlqloKDAxaT/SAQEJvGyOdyEk5BXKQCIsWhLR3v/7j1FVL3/dMyoNahX38VmfkSAKQAMWvDkyRPqHDB8rWH9BthAa4Aa8NGIkbjIERSBAvO/ogBw5EFQgMod8bFxgSgA+AQAbcmgGpl8B8LdczgcZWVlNTKqQzpLGCZ6Hdh/QFHhG8X3sVroK2D3TUHh+LHjPPdsn65BoB5YVJMc9uFQU8pegkl47uw5iufFEDHAMjE+ATDhyTaZGQyKAJDMpELgeTtn1myOOELkoirMxmZr3rRZ2GjPYCiYAsBQMdQ7XFZWVmTk/PHakHBW/KbImH198cJFdpzfMEr2/o+xpSanFOYXGNABioD6OzWr15ASUFRgeZLy0wleUnbe5CZtxeTZt28fyP2xOK+/ONgO7do/fPiwwpmmPhDY+8ee6ukZ6vSgdiJdJj4H+G7FSl8dMydAM0yrWs2ECgBMv8cPH7kitDoHAwLSmUkFwIzzUI6+g7Vfq0bNwEeHp2tRkVT/WDrxiAoolV+bVq3DQ3UGo8EUAIZAQW5ykydOUjAFMxdMCbYh4ezKpctqW6yOZITX7du3K85eJS+XGNuXU77Q/dZmhFMoQEP6xvh+LiJlot6CrPbbt/1Gu+s+oX5qA/sPwKsD6wCgEiAx98C+/X5+6+vD3OwcsJHLZyNKTzx8o3t37lZ4NZMAr45LFy7aTclPED1DybfIYAn8uukXsfREINnJqM1DXBwQdbVn9x5BrWv48i8bNymq7AXSLHEAyxAImALAEBAUGcqbN22GJQzMFyLjQABkpt+2bvNNDH1w+OChOD4WZxUkLTqII9+8qcWh03pwunr37AWif0WWNpEycs3psaPHmH8Hgh4+efSY5+zqgBlceYcTc5v6StPpXwvdlrU1vVoaWSdL0Zo1aQr+bCZ3VlH0bdnSb00odfHiFD1+9JiZKckQOl6/fp0YnxBYgUJq8zBWTg2M3k+aMDGQcSn28VYtWuJdjw94Bx/Yf0C46M5gLJgCwBAoMO/Izc4hkwaoXQ4s3cD/nswEGo4TgAvnziclJMqZDaOgVBM+Aej//gc63tG0ABegj0d+FEgWIFwZTcxUzdWsXsOAQO3QgXq4eOEicVxSfj0czUyGNYMN77/nL8gf+rmmy3M9jhw+wtdGPm/OXPVvzQnSqx5nJjFba/H2O1avRc0QCIYMGiy610vFp6lPPEXjiBNju41bs2p1sANEeixHbOJ4+6vw1qM/GRUOgjMYD6YAMFQAtcTw++7/qRliZEj/mLeGNc7p/PnzqckpsdLBq5TLWY4EEOzcp06cDN/dzQWna+oXX5KZQH0/F7GibUwUnACgdu3KVdq9rxjl5eX16tR1+xPbJDUGGln0jRMdeU+cOKG4gldXNPX7P/fua1CnLq8y412+eEnxTfPrAEVFRQlx8f5TkdBq639eRyoAZiYmgwbgB7prx06TuwABV4QTgMMHDwUYSoQxsP8Ar0vM/zkA+t9pX05VX43BimAKAEPFUCz1WTNmmpAh6tUQ00dsEbUnT56oxx4gicgPSdcLwbn53PlqKanyvTjCZCsJghbN2aIZy5Ytw5uZn3rScD4ASiZ4z6/IXK6grQkF3D2//4F99P3sqTAuwapn51cuX6HNxpyfnz916lR0BaxNNa5fT/cRhQ/4wZ04dlzwCrOZob54NFmYr0pKamFhoQmnGYNeIANkE+MTJOZDfx6q+KEtGieMRiv90aNHQY3unxs35QqMQd43xrbg62/UF2SwIpgCwFAB1LbGrp27mDM+T5cGLviIzf3840+hSP9ecfToUST9kyYWwnVbsuUg6cdleo9tHfHjjz/K08mfwAdCPy6RhlqPbt2xdzu+mtmIhjrpXwEg/ehwdqDBAwfl5eXhiwQ1qKtXr77XpSuQCOnqYRhTGAEjXbp4CdTEMIHgRSgAMbbpU6eR/WSIVMDz/XDwEN5tnaG/N5GNDP1PSUp2OBwBDgpeIZWwNgVg1XffkxdksC6YAsBQARSp7srKyhLjEyL4BADXVenYvoOaCL7o48coKP1XueO3rdvi+Fg/90U37denL1kjtjJw2N9++y3AEwAceQIaGiJmYX6B2VIxkt24fvUamaXHfwPFBvQcwXjfsNGVS5eD8jYhv7Ata2uNjOrnz57TfYAGAK0CTi7DRJ0bgB+InROey/2796CHZpt1DOEAWkQ2t3WG+sbkyQ9tkBZZMJ107tgpqHE9efIEMc+YqID4kqIh3pu1eQtchx2FWR1MAWAICHipXzx/oaJ0jdZuwmYvHgIg1o8Gqw/1yh1LFi1WuDTgcwCpCIDo/nHj2nVd7mgVoBl14MABvMv6UQDIKgF2WUo+9NdBl+q0xDwb0mfjP61QAVB43OJKYeifhPjYjes3uIIfDVDg9evX5iFFgEAdLisrS01OAbMr3RMA7JgHboHv9+1nibhzhhCBH3Fe7ktwqMN1NszTgKvYRW/VyRMnBTUtIewKik4Ge190u78PHXYx6T8iwBQAhuDw0w8/mjM4T68GBj8wx37Q7/0At3xfkZoIr/NeDRowEM5bsdemfC85I6Qo8yE+7qp8LPXkyZMBugBBnDTWlzhVihuXmaj3MicXzsr8KwDg9oM1aiKYXrQ927ixo8dASbgKrc7mGXsouHjxojs3Lj0FgMzKBZPtrwN/0qYNg6FAC6pn9x5QEID6xqTkGzJXQTMTbcqBD+q/5y8QX5IHpeUEoLJZqSIYTAFgqAAKqeKTjz6O4AAA3n20GgNCeVC7vlpEO3fmbKMGDXHOZrisIl0SSLQZaelIZIwMGS4oXL9+PRAFAL4jJ06Vnk6nDh3RFQLxfzUM+AkuX5YpBeP6dWXBbj+qXP6ShQ/9V8t3Wty9fSfYDlgO0PNV331vk9S8aJzxyfiG63ODLaBu7Trl5eWKrjJEMOARZ2ZmavOVN2CfwhmKTp48Gag93umaMW06SP9c1FsaFBt0u5z/skkSMVgXTAFgCA5NGjWO7BMAKSmnnAG6Tq3aOTk5GghVUlT81bz5ghghy6xy3k8y84/b4puVleWqlCz1yZMngcQAkA1rUPF2Pj8/H1/KPNQrLy1DIqONOKzws6GSKVDVuXQh5iE1OWX3zl3keZSfgBPrAvV/2IdD5SVDMwiY83y/eOEi2rRhMA54HV27ds20CgA+AXj9+nWA43rx7HlCXDx2bNMwrjg+trJFqUUwmALAEBBgnWdnZ5Ou2BHZZHttND7979Kp85s3b4Ii1N69e8Hwj8mlkPuhYRPjB336qi9SSfDq1atAYgAUDdPzwIEDiguagXq7duzEAj0Y+H0NxO307/s7+AvTvpxaWlpaYUi6pVGvTl178PNBfz5A/snZHz8U0ixanrgMwaNOrdrmVADgXLFh/QbQz0DW/hefT8HlR3Cd46Ba7Zq12DKIGDAFgCEIHNi3P6zZOcgsKNJ7tzEeV02SvuzpL6F/I22xHd9t9/TxE1+Mz815na7DBw8JmR/9jpEcEfqzWpWqz548Newhmg1lZWXYBUjDvJo9c5bLTIKvUyxv3KlDR4544jrqzOjKeLZ4HbNHFIohQ9YRL549x1ETYV3dAbRoSMyFGq7MbZ5pxhBWkA967OgxvDpYXw5Gojg/IXZo5PARAQ4EKbFxfGyACTx8ba8d23dgqyBiwBQAhoAAa/6refMFaSY8+bmBpcbKgZ5kEB5k5sFZdGJld/BYwrtG984o/kRi+urvVxUWFnqlTHZ29trVa9q2bmOvyOKLxwUGGPTNXTt2Wk9S0wM4ctomPfoYDT7fHdq1pz0OCdK+6BTqWEHIMi97kOuYNQtdKr1a2qFDhxT3jYBdGQ1h/959dnl1hFvDr6i5w3//+N/vroigMEOAINfUll83k+uXFP3pzk/YaFYuX6F2C1Qnp0avY0aN5uRgKntFp448Lk4S5ZG5bsSw4VSeCEM4wBQAhiDQu2cvSB+m7wkAKb5jlgrxsrws9IC1Iz42Li21Cqkn8GE4CsAaBXllm+yKPXzosGVLv92wbj3aGFZkLp88cVLLd1rYZTkPJ6sJ5Bbo++PHjqvkuQXR2KumVoEtTYtiydmLCgN10NK3217fo6fZr09f0lNOdwUA2qIFC8vLyyNs5nw9/yubnHwzHIp9ME2YjYjONavXKCsppU0YBmp4/PARXr/Y5ITZOMX5CQFmx44cdflIEUa+v3LpMkeEtvPyYbuv6+OlB4Y2/KvZs2errx9hXKjygCkADIHC4XAYmZ8bp14GPtWoQcPvV36X/eK/Lh06khwqHNK/Vz4I/0V6KCka5ssKVyVfDf2kTavWJUXFtB8sZaDNA8lYvNagT0TG0ydPKS5oWM9dql3w6uUrCp8f/xtt0OO1RWNtE2kaudlChLrT2IGHD3169ZbTrkerF6ORDWYjIjX4mLkigrwMgcP9uB1OtPsodgQzuAABn8ERwP7nZ68ePTnJAdWdmcA/X+KiohXbK/pz/fr14aY8g2FgCgBDoLh586ZQfpWz656dg4yOjfV0nEAb8KABAw/9ddBZLqR6LCsrS45PUPwkPNt/DBqpJ9OXw4Jt0bIzUjTpoUT+WaECgIaWVrXaw4cPFUWsKhtg7PXq1IUtTYMLEHpSKzKX03WDIR/iyOEjsKUNbNjYHUiXZouJgvM3nJ7yzJkzLk89xIozCvpcJSXV7naQC8vSDrhFgwKAq/8yVFqM/mQUOTcU5wDU5meMrVmTpi6/nj+Affv2Yecf6HwgCgB5Eo7He/jwYRpPgCEsYAoAQ8UAVrJ1Sxb21Q5XDIAoLYHFNDE+4fNJkx/cu4/7gHD16lWFST58vFjlXxStEvGlTGpy7TApOX0FWV/E/43jY0+dOOliZkURLd5+R3Php1hRRcSXMpKe6hOAe/fugXkeZiaeqzqeAOD5Jk0ndBc7v2bNGkUpOlPVRggQDx/8K7tOmUIBQIpWz+492Aqt5EATYP3P60j7FO95MkxrfiKuMmbUaLKfXt+XlZRCSjrMl8C0H4hrosL8j17v3r2rvgtbIxYFUwAYAsXUL74MJVtLhQ2LzvGxcV98PuXFixdqtrJ+/XoFb7Iryyfp3Eh3T9IcQvyXO5wXn1r4z/qCvrN922+MaWK0btlK2tKCVwDQU6iRUZ1Kt9U77qQJE8OqlAKV3CdR4pKBVTN86LD8VwE5A5gWe//YIyvPUiU+qhmHBQr/tnUb9M2iJGXQDPKJX79+ndxlaNv+5fkZY0Oaia9u4zfLln6L49NwLZpA+Cr5Hn4Yx9lLSkpcbDlECpgCwFAB8FLv3LGTzBf0jwHAcnPP7j2EM3end3vGuHHjSC5sJCNWJIJwfy4mRcXSWIUnALyYt6Fypv1RA55sm1atQ5lXiOD379+nPRTX8+fP4/hYntAVw9OkMyiYeHbikKFp4ybXrl2jTQatcLoWLVholwQUOFLjdD05CZrOqSlJxW+KaNOFgSaAQZWXl6ckJKotTXSDgNHquH71GtlPNR49epSSlIwtUxoWFGlia1Svvpo4DNYFUwAYKoDkUVBWnhifILm/6+3SIHnMc3bwZPDdFdc7zd+mx3BDbaAYfP311wraMiAFAFyANJ0sCXNyW9ZWfDXjqQr3mz51GsWCQSA0JyUkbvl1M9afLTTBUFc/HDwEzP9oIIJuw2mpVBpsI0/tyBwpUoYuBgYRXTp1lne9aDkKLqBkD+FrqckpSDNxeVvmOCXxwP4DtO7X0Tj1NvbHG/BBf+MpzxA+MAWAISBcv3pN2CA5QcjQVwEA5oL23UULFipuquBrebkvdQymNKZh2wkQbd6cuV6HVsmBNlfYcjSdAAihwzOmTacr8j5//jxRCE+nKBAIbjOgZH42/lNftavNPPHAU1k64hBzDRsQCUC6UdmiPVIPHz96jDZJGMyCaV9OlaeHND+1VdLVsfXp1Rt3z4s7vtP1v127K/RH9dUkv1Y53QWErs2ZNVtNGQbrgikADBUAggs3//IrNs7pfTQvBCTVrlmrtLjEf0/27dlL1SUg6HFhWoGJccmixWArZrFTCvTs3iMEFyDh3KBb1/eo9BxibdFznD59uiy80plvikCUNq1aCwH0Dh/FCsyH/Px8OUgxBp6pbGQNL91wYl/sIAE6QP269SDzGMDk1GMIN7ZuycInAPJZJSSBoLXeY775SjpM9noC8DInt1aNmiFE0bj9DKWcvDZOOF1kiCAwBYAhIEyeOAkH54VjD/5hzVpSWFEAlJCZ02dYSwHAxRpRW7t6jZHPyyqAratHt+4hZJcS1IaUpGRaJwDoji9evAAHOeouQDwRTF8lJXXvH3u8EtyEOHv2rCx/R0FNaGNOAHhvoTtoNs6dPYfsnmnpxmAMrl+9huenlCLWEBc1Xw2J438f8pKRE0/Uj0aMJKR/TSerni5AaPgXz18wluoM4QVTABgqBuIpHdq1Jwv06Nsy0tIL8wvI2ynuDm9atWhJi9tqalKMJhIN/7drNx6Lr9FVZnR/rxtQTLMCgCbn7du3De42fprTp07Dviu05htemyCmYIl21oyZZWVl5p91W37djLMMQwwAeqyGWVjJBF9At3/++Yed1DFglJaWxtp5XqiEI6w1UFBpKvx2HiqgezV87N65C3vqymVqgr2F+wQAliG6WmFhIR3qM4QHTAFgqBiOsvL42LjwiTjz587ztcVi7vb48WNLmf8lp4JaNWpiq4mvUC0mXvTs3oPXWgkYV2zdtWMnlc4/efQ4IS4ei630ppwkLkP9AcGPhbMLsqwtumvnLs+fPjP5NJs3Zy4nxcxEyWdBFKoBYAcqX1nVGSon0Bxo1aKlmHouykgXNV+tS6fOZN/IruZm51RPz8AOS2g1aVKkxZgiMRYfAv8a1m9gxeoiDH7AFACGinHh3HlcoEd3lhfH2Z89eern7sDdNmzYYDkFoHPHTk8fPyFH4Wt0lRw9unXntSoAuGLrrBkzqXR+0oSJZG5+WvMNB1Hgkzr35m2LrpFR/cjhv6nQJ0AMGjBQ7WNtvIAFCsDq71fRpgeD6QBFviFGhXSOp9KQwuzdfuR0LyVcp1JzbBW4AAnWDRs3eOAgOnRnCBuYAsBQMX764Uc5R57+LG/Y4CFSaKy3W2Pu1qdXb2spALNnziovLTPuIVkZurgA9e7Zy/ieP3r0KD6ON4MCwMsalKLhjsXa+aVLl5rWhte0cRMcBAwuFgYrAFJhQcTlbNx/z1+4vDnsMVRmfPPV11i7FlRrqjEAh/466LWTa1atlmt+yQmLtBpWYIyg56B/v12y1GCCM4QbTAFg8A5y5xv9ySgwjIFrgbZ6Ijw419rcud7hmseOHavQE+b58+c8Z6d75CrlV7G5DavAIkHMgkEBfWpkVP9z/wEjHlKkoG3rNpqzAMlirhBJYnzPPxv/KREbQ8FlJcCGc9H269P3ZU4udJ6KB5rX25WUlMTbeXX1NGMKLXHELEJ9eL9vPyMJwmAVbN26FVeqJuNtwtzkst/i9ieklYuKjuNjCwoKFN1DK+vChQsJfKy6YJmGRpr8YNS+VA4G64IpAAwe8LI9O11NGjUGFyAySE7bRivX95H+bPH2O/59beGThQsXgvJAUQGQ8vnIJ79xnE0W+KRewReGDx2GBSyGAIEUANjqtCkAOEYtN9cIyuNZevufW7F2HqfHpZsW0M+iw6o7TNE6tWqfPXvW64ho4datW+GsneyvSWSRkroIn5BF5RgYMNCqkY1fFBQA2eokHFJ17dzFpVq2r/NeNahXH0v/IS4oOYY4GgIA0Bp59TKPEuEZwgWmADC44VX+hvJbZPEvOCXXyFNsbiEP/fnzjz+p74sNk/AmNzunamoVUB4oBlkCEYjYL7JJ8b67d+4y6llFFOT8TloUADiIhzo1J06cMEaWhbsMGTQYtkYQ/akXBvK16BT1rTgxhcjKlStJdyC68ej79u3DHTZeE8CKPbp1fGxc/qvXVIjAYHLk5uZSPwGAm3opmlnuGDxwEKffoRnmbAJ3tXGNGjSkbiZg0B1MAWBwgxQCsPx98M+/ZF6gYA0aN1rRZCvs8anJKfn5+X56goA24+7vdRM9gzlSeTC+4Yz+OD+J5KyMxsLZp0+dxuQGzWjetBlsddpOANBTiOPsWJ8MH8gt8NyZs7jqFkwGc7oAEZ2M4T3rXn04eEhenimsemvXroXeQgojg+kDJwCCDhAdM2jAQNrEYDAp0PJPSUrGdnG71pPwIJsUE49jjtEsPX3ylKJvX82bj3+iywoCFoGr2YwcPsJlgqNCBn3BFACGCrBowULS5yfW8zQg8CYFD8ju8ugKEz79DN/F6+HD1atXkWhol12PyPgB4xuce+CKobj169P35s2bfgbCUCEa1KsPW502BQAbxr74fIor/I8Aro/0UkITNrsLEKmxk3WvGtZvcPniJcXQDAO+HdKfKdJHeoKcQJCtW7KMpACDtdDynRZuu7iBCgBZd6xKSmp5eTnZK6JKsc58A1ez+W7FSra1RR6YAsDgBeRRQN/efbD3P09EE2rjKXIEraAMqBPk4/vm5eXNnT1HdrDG93X76RrfBOcfQu5HrVOHjsePHvNKOoagULN6DdjqQlEAUOvRrbsxHf5z/wHZQmaTPW5jtBbcCW8jo3fcyxAfZ8XYEuMThJMT33W4wwe8WIYMGkyRPnjuxcfGvX7NzvEYfGLAB/2JoH/jXIDgBEBI6BljG9h/gIvYK8+ePiNX6dGzybFDMZBj7dyZsxXRhsF6YAoAgxfgjbm4uDglIVHaKUUxIpTjRTKQt1WLll4Tf5aXlq367vuMtHScXQeO5rGrNy1BAVKhQevYvsPevXsVWQJZ3VDNqJKSioWwoCeVXKgSterpGa6w0d/9fMsdaPYSqTYkUYDq5Ky4wfq1y6I/7+kU9PHIj4Rq3FRmrpNmkW85QEKYe4MGDGS1+Rj8YPLESXa5Vh1viAKAy/FKO6CN+3HtD7g/N6/fSKtWhYt6C4ei6XVf0jUuKSFRcebAEBlgCgCDPxw7dgxLD6EzFI6w1wpczHOTLSgo+G7Fylo1auLDR1zEhHrVFV60hSCG+EG/948dOYp67it5ERMdAgemVWJ8gkhk7TEAeI7l5OSEr5/wZuuWLCwEgC1QIVKbqmG532MZqr6G5nbzps2QPKE79QKgryslKdkr6Qygp6y5CXMvKyvLxZYwg28sWrAQ702GuQCRNT3QHe/fvQedefDgQd3adbD/IZjq9VwXctxdn169aRKdIWxgCgCDBIc369+sGTN1ZGSxojcCeB28evVKuofT9eDe/dkzZ6VVraY5wag2BkfG8kLZc+ChuNnkogepySmTJ0++fv26i3a+lMiDw+HAfthaFADZtQye3fmz58La26LCN2jTpVvwKxwN9ITk+ISszVukwnyqMli6n3HBdV69zDNs1XuZP/KjjI+NczMlBgYV0HTduH4DcBsiB3R45yeu5wXSf+OGjaAzzx8/adqwEakh66st45ADjigBxna9CANTABi8Q9j8yx11atXWj6dIJhMkVY8ZNVoQMhzO/fv3f9DvfY7wSzZs48e1EsH+h8ue80SgM5L+27V995eNm5DY54tKBj+XyMPr169JK6yG5wiPEoLVdE/irnjEaC+UowCNm6vhaDhZuFQBV/SEgZk/8bMJxcXFXocfjgn/z42bFBUA8OVAAwfXagYGP9i3Z68QVBODy78YwAeicZFBdOvpU6ehrfPJE0H6VwTm6dvI5AqnTpykTXiGsIApAAweIM3bhw8e0nVjjsb+x0hKW7xwUYN69bHcr7m6cIiMFeQeMrcPBE02b9ps6eIld+7cURPH158M2vDw4cMQTwDEhyhVrFRnyNYL6HE/f/osOTFJ9gEweLqGpWEZQiryLa/Qtq3b/Hv/QZgoqcChvw5SVACwlMPy/zBUiPNnzxGOrEYoAG4/Q/GE8+9Dh2//c6t+3Xrh9o7DCkBifEJpaamL7XeRCKYAMLih8GXv3bOXvhszmBixxA8ydyyRSoU31Is6mozrhV51aNceSZA3rl2HpCi+KhMz6IgbN26EogDI0yYKgjSEwyW9gR/6+LHj8MGRUbPU6AaURE+kWpWq+/bs9UUQXRYCXOeXjZvoKgCcWBmNFTpl8A80V+/evmOwAiDnFhPOqJMTkw7++Vd6tTTFeonj7LrfFwfdsQCACAZTABg8gPf1K5cugwe8foxMykWIxAvs1wjn79gJAcyQRlUCkoqcozG+0/ztrM1bnj9/7pUUzPAfJgAlz507h7Pph+BbL6lzXTp1DscDQtdEK4Ln7BD+GxkKgELZxvlMycO6eXPmkglAwhHvvvCbBTryGQ0zBw3z/b79FI9bl6ExRBhys3MMVgBwGlB005SkZEiNjVMOoIakf+zOp+N9cczx8mWZbDlEKpgCwOANTteIYcN193WW/Lyj3iIDm0jOZaD0byNPAAS/ETmxj1MVD800gTBB8jQ7fDiUEwBZaYwBda5m9Rr65rKUnrLT1bN7D1wDKDIUAAUZyXXBE05xaODZL/4Lk1qFXidNmEhXAUB3X7duHVvODBWivLTM4BgAOAEQcp3Jh+c2b4fkWA3QqwkhVSJHpZMZjMEQMAWAwQOwC165dFmWxXVmcHIGZcn8DwkccYUBY7MouhWAbVlb1bl9vPo5sGT/umP79u1kMSZtTxMmFeyOQj57vbH3jz3g+i9pGhGUBUi96BSZQxFJa9esdfrkKR094sjlNnjgILouQGiAz549C3FEDJUCTld8bJxce8Q4QwBOok1G7eP/DcfJOWQBali/AZUSgQzGgCkADDKIZd6nV+/Y8Lg6yFnb3UeoZNpNzN0MOAcgM7ifPX3G5Tu/p14ezwxqIML+9NNP8om2dsGafJq3bv6jbyeLi4vRRghn4jggL9zz0/hGFvlWxOQIH3L271as1Mv1n/yzU4eOFBUA9EA7tu8Q+qAYKgWcrqqpVYx1AbLxRFku4D++Ko7raEGDnHhTJn9OpzgggyFgCoCp4X+7Ff43tMXp9foH//xLjs2NQFcHksFhV4fs7OyQ6MigGU7X4oWL8Om2ZkEQtivwXD/010HdeicukCWLFtsJw1uI9bAt2oDCHw4ekv/qtV7kFUnsatq4iZEDUes2y5Z+q37oDAxe4HTVq1OXNFpRX5h6LQcvSz7G9tdff7HlEMFgCoDZ4XX5ORyOcJji0J/Fb4oaN2xkE6OLcJBuRDbZoBuTVrWaLzozhBWgwU794ktccM2re2sge5idiwGrPLrCLxs3aXuaXo+Anjx6nJSQCCpxJZT7yfUCiQgRf7h27VqFZAyE1OI7V5WUVMN8/+Q8BBx2c0IThvRyZnyAwR+cLtgfI0YB8NMS4xOKiopc7Aw8csEUADPCqz+6/M516sTJ3bt3h+nW8+bMlXJ0yoGV1NlQmBooAEgIeK9LV8bdqMHhHPXxJyFa1Ozi4TgoAOgKC79ZoGMHhw8dxsnVBng580Zkb/w+WjSUWoNK3kLBYBEks9JQJLu0uMQwYpLO09hnulGDhtjLmfEBhgrgdLVu2YpMHk17VYaxsdJ4EQ+mAJgdeE9yiHr48aPHkhOTwGQVYgSedFmHA78/e/oMZBnDW34EuwBBIBca7GfjPw3h+TCEBqerX5++oR+pYwUASagTPv0spB4RyvbRv4+APsyTriOiwxL1CWxwI6MsQPSZMvnzshKPCkEBciSsJyA8e/KU09V3OZBG1h754vMpgfecobLD6erYvkMEnwCQof+/bvpFGnTwij2DJcAUACvh3LlzSQmJKUnJznJHxd8OAOSSfp33qn7deljcwVs+dZYUpoaEOLBorlm1WhdiMmiBw9mmVWtciE3DhipVlxPzZINtvv/7H+iiG5eXlr3T/G21bGqrrCcAUuVs8RAAXpEw9OTJk1Ce/41r141UAMhDAHAB+vPPP9WPnoHBO5yu7u91qwwnAHF87MucXBdbFxENpgCYFwq1+969Oxlp6YjptG7ZKvTMXAqjXXl5ORKbMEeT9+NIPgEAmzEa78njJ0IkJoN2OF01MqrreAKArtDynRahdkoMTlj9/SroD+n6r0iRWZkapECV/aDkuG30+I4dOaqkXiAUFnHm1GkjtSnS9o9eUxISi4qKmIszQ6AQ64FEUhAwbgqehuQBF0t7HelgCoAZoV5sBQUFLVu8DSLOsA+H6nVx/P7LL78EizgW+rmotyJY+ueJoMa83Jeh0JNBMwSxq9xBWpQ1W9RIBQCiujV0xkWsiGdPnqYkJXOy0z+ZabtSSv9AhyjeDs8oGudBF/7k7Csyl2ugucPh+HP/AWNOAEirPy9rAoM+6O9VxGHiDoN3iAoArpEXkScAwOi2ZW11MeefSAdTACwAtPzGjBmDc5BP+3KqXpeFN5mZmTabjdzU/ScbjowGImP9uvUYj6OIF8+e461UrxMAdKmysrIQOzZi2HC7VArDvS8aVqTChE0QesBrTqwQRFT0k0yhQwYNLigIogQbrLjftm7jDCcpVgPW//iTi619hsDhdPXo1j10fmXmhlZHYmxcwet8xdDJcEGGyABTAEwH9W70x/9+BxdnsFgvWrAw2Cv4+d+li5fgjPiVrSFiDh44SMf6pgzB4tKFi5wYkI0DTLU9R/BIAcEO/fn86bOguyK61TnEyhqHDx7i5CtTm5xy3THIcApr34QCB+kL0axJUzKlphtOxb/uJfbTDz9CiWWDumqLFhUYG8yTBw8eBD1PGCozZAUgYlyAbFJhkxhRpZdONkL0MmCwCpgCYDoopM+8vDzB9R8kAFEIWLxwkddvBvUnQmlp6fix4ziiJFalapAC/NslS70Shx0LGAPwAIEZGIoCgH8Lgt0/N25q6Aw87uLi4kYNGuIrGzMVyfdkBA5Y2cHubhNnLPWFoyY+7hV6n5yYtOO37RUSGSPz22WclG8g/HQWDzEgWBzJOk0aNdYwSRgqJ6R563BCEDDP2SMjGYCc4iwKn+ahT3bt2Emb3gxGgCkAJgWOS/vi8ylgIcNCwDdffe3HaK3+L3xyR37n3/sPunTqzMkFxsF9orI1xOn+PnTYKxkr/JBBF/z84092MYePLgoAru169O8jwfYEq3wLv1lgF2t+0c73TzjjoX7ERJnT4kgmRbXLfhHTvpzqKCsPhOYLvv4GggoM6Conu4qBx9GXU77QMmUZKjOcrnZt34V9EyIBqC/AEJuc9iMKQvzRiFKTU0qKimkTmsEIMAXA1Lj9zy3EZeI4G3gCQBq+iZ9NgP/1ZeP388Yl6gObNmzE1TdxCkXqnMjgBswuPz9fTUAXq31oFKDwnO4KgAYLFjzs+3fvxcfG4elh2LrwFgUbjRvkqzWnxZHsFVjW4Vl06dT5xbPnrooO06ZPnWaYC5BNrBgdE/0WL+hT0fv37gtl6jJURjhdkLY4woKAcdFPNKgQ66gwWAhMATAj8GY5ZNBgTtbOxZNrYYl2aNdeIdn78fZR/Nfx48fbv9sOfAlwJBw4GVPnQYazPO7tZs0VVGKeP0YC0fnjkR/BGZReLkDw5/qf1wXdGbE/vXr0xJdFQqKomYR9Hnr9hCNiG8hESdQXjlfi2yVPYneCVPRhrRo1z5w5g5+1V7JPmjBRdEIwSAHAjpTx3sIcGRgqgMPZtnUbvDBNuB41NLt0BCdx4HNnztKmMoNBYAqAeXHtylUsG4EbAGyTiPX89/yFHyFV+V8OZ3lp2b49e8F5URIm5OSGIguojC5AiLbjx47zSjT0Pi8vr6SkJAxPlcEDXTt30TcGAJ7s0sVLNHQma/MWTk4RI+vbBq0LdamBH9f+MHjgINLWCC7s1BeOmvjCa1S0aiBChlAkZ//840++mBX6fNyYsca4AEHMD67+0a3rey6m6jMEC6erSaPGOAuQPSISgsVKGng0Gk7zps1ok5jBODAFwLwY9fEnorsqnM1F41BdxHfmz53nqjBxtVMQ/S9duDhrxsxaNWq6xYjoKHxB4ShA9L6ohJkNEbPb8utmX8S/evVqeJ4qgwfq1q4jy7XRmo/U1ScAU7/4MtieZGdnZ6Sl8+D8Y8P6tnECN+kFNHLoMFjRJ06caNf2XZ6zy8QxnQKgfgoxUdFwbin6Lgpy0mfjP0XqtNfjtaFDPjQyCFhKpxZj06YiMlRaSPPW6YJynBGTBQinHECSBuIwq777njalGYwDUwBMimdPnsaK2z7Otw3KACzaOD72yBEvYY7ApP7999+NGzeOHD6ienoGth0SopW41HFe0UqZAogXefejfx96Jf7Lly/37WP+wWFHaWkpJ4dmhq4AYGscej9uzNhgOzNm1Gi4u+e6MO4EACsAaalVskXveQkOZ9bmLbVr1jLMVz7YnvOEPET4RkeTqUU6tGv/9OlTGBCpA8Aph/EuQKdPngp5/jJUPjhd8bFxXATWARB4XUJcPCuLWanAFABTwulavHARLEvBGCn7/uIUgUJkMB879Ysvz54+8/Dhw3t37v65/wD6Sf/3P6iRUZ02K5Ga1FXZ0QgLVSAWSEVVZaGN8EdSChOYPWlmuLgn5IeNGjXyRfwvPp9SWFho7COvjLhz5w7so7qEoZPP98OBg4LqyaFDh2gtE1LZgMXuNYAhPz9/9uzZPM/LhoBoQsJ2p7bEdODkDN+0xoUfCtZtamVUP338hOIcoG/vPsbEAIBPF/QnOTGprKyM+f8wBIvS4hJssDD4hDB86wK7Fo8ZM8bF/OIqE5gCYEY4yx316tSF0Bxw1OE9RWQcF6g8dvew9FNudqX8EY0LiGIZhWwpScm9e/aaOX3GxvUbDv7514ULFx4++HfP73/IHgLaLcRkl/CbiRMneiU+UqUmfPoZY4IGYP/+/VJEChcTSritOoXO+737BN6NNwWFOPG/8U1WfiQVt0unztArhaAMaalu3brVp1dveZm7CwVIWzjO6yU7KFPkBqrlL3ySwMeuWbOGHF2Pbt2NcW0CIgNx+r//gc5TmaFyIPvFf5ysTOJK2JZu4APMi3sr2nNpE5jBUDAFwIw4efwEZCLHHqvKzUzVSH2AOk/hZccAoqqRTWEywaeobVq1XrRg4aVLl8rLhcThCsn74vkL4CEAHooaRkfKQCCUgPq0Y8cOuIVb0nI4nzx6nFa1GrqpuicMumPNqtWcvAmFcgKgVgDek8XoiuFwzpoxk+KqwaW+ONG179bNf1w+EvhiL+RdO3bWrF6DVAPwssInhJxngk66LZY46ENdGv3JqOI3RUB8qKuKnRvD18Cdkhc5z3crVqrJy8BQIe7ducvJQrORSQLC2STFuGP7DjBGtigqD5gCYEZMnjiJk4MRQTJQr1vsa4vrg9pFuZZ0JqbSPIV+oUGXSIdg1OGUpOQpkz+/fvUaHrVXoQdSIUGiUm3qDfkrnKQcESo3O0dx6zcFhUgbQa3CIsoMuuDLKV9gBUCUz5QzR3Nr27JVhXeHx4qUPZ6zU10v7tOthd8sUPfQK/Ly8j4dN15OwSmtLJCz8QmAGcwB6kMAWH1olQkROE6XkScAWAG4cuVK6LOXoRLi8sVLoADg5Bx015ceLRoSdrHqv5UQTAEwHRwOR93adXhZAQC3YDuxo4M8jQV9RQJB2tzExntGNOJPwMiH+p8YnzB75ixB/gaDJlFySy3xXL0q5UKFKsga0q4pChXBRTp37KQke1n5wP4DUD+zsrJ8dYZBRyDyvt+3H96ENLsAeZ3zjes3CKQPJSUl7zR/m/Z6kdZ4syZN/WeeVS+TE8eON23cBHyoSFKY4QRAwaZ4t4eSVHA0vVra0b+PQD1yI9KAyoUUq6SkQnF0tsAZggKaMIcPHuLcymS0MdmrwtwE/lOnVu2ysjJypBTpzGAYmAJgOly5dBlv22AZtcmOK6RVjwxsVctAFDUBr13CkvfggYMeP3wUIH9BX7tw4YLEcDmNLkCk1oSlomVLv8W3gDefjf8UfV6vVk3GBw1Dg3r1yU0olPmmaDXS0v3fGtTOxQsXyauJ2kYOvm1oaZw6cVLdST/9h9eSouKv5s2PtfP4SJCUuSkGAdu99QROArEtgOfsSQmJnCH8CtzMEJ0HfNDfj8WBgcEPtm7JijAFAA0BDWdF5nI8RrYoKg+YAmA6LF+WiV16SBcgD49/z4BgRc4cuucAuGOxnhl+0E6/acNGl9O7q48vHPrrIPa51ObSgF2keKK06p1bt933cDgnTZgI5wwrV2Sq+8AYYjhQWFhISKuQd1+3SZiSkOjn1mAAvnn9Rhwfix3MaK0XYW5z9ulTp5E9xMdi6rmnXj7ozblz55o1aaq2+lNkBWrnH/m9FECJNRZ1uHA4GjxldDvEYF2EBqXPbGaoHMj8dhnOAiRX1KGzvvRr0VVSkwsKClwyY3SxdVFpwBQA06H/+x+QJwA4CNhX7mHOUwewE6kA6TZsAkSvdWrUvHThIpb+Ay9jvOO37SCiaVYAyF8BrVq+04K4nxBxARWCUlOSXr96qegJY4VhwtXLV4inKZ0AhCIIKn7r/+7lpWXt322Hb01RAUC3btKocVHhGz+yvhrq/0JXQFoEThlMPRZI0YgspZLplFRXDOgqZJpCL+fOnNU2YxkYvpzyBd6PIuYE4Kv5c12YpTjYfleJwBQAavBqCEcqeLWUVF51gG6hpqijhNhl3dp17t27FzR9xNflyzI5IkuMBoKQGRtA4Ph2yVKJ2mXln43/FOyR6L/mzZkb8lNl8AdSp9qetVX9sHS0BPvvCZoDsuu5cRs5LsJNNo7jTp065dJJ1Tx69GiDevUhMhgXEPRMWy6Ol3Z9ACoNkSUpIRECLZhiz6ABQwYNFrdmmmeGmpvdbUB0VxFJTEzMzs6mTVcGOmAKAH2QW9H169d5bym0LdRA4EBiN9jbMtLS796+o4Us4uvEzyZw8jW1kYVI1xANbscQhFBaXPLh4CGc/IWUpOScnBwmFoQDZJw3xoL5X5EPVHddNz8/n9Q3yA7cvH4jPjZOdECKkcvSGaNp44T9bjVg2rRpCsqEOAnzX70e/cko4SjAFq1QAOxy2lDq2YGoNDTqPr16h0JbhsoMtDA7degICxmSFljOQicfD7rj8tX8h6HygCkAlKHY7H/88UdFRU/eavoAHLULHreI0dj540ePhSLQdO3cBUSZECwugngnWUNjbL179kK6xauXed26vofdD9DFmfk/3FBMgyEDBpLzXPet9PXr1147UFYmOP9ILnNRbxmWz1tWNuA9B6EpTRs3efPmjZo4oVLYKUQrIp0WvN7tnql4LGq/DL2hJ77wmwXMtY9BM2rXrIVjlqwl/ZNRy5BwGTLyPX/+XJ32mq2OSgKmAJgCeL1NmDCBlIosJPeTjAY8alBbtGBhKNtteXl5anIKKAAhVIqNxlZe1KX/7dr97/0HzZo0JTosZAYkywIwhA94JjSsW89z2ui8m+bl5alvioDmJBEx71FJN8wtGjJg2uRsVGhinz55St3J0Ddg+Pndu3fbtGmjyA0gshSaMQ8UG6LD4YOHvM4KBoYKgfajOD4W1rIFVxDhASizoBnTpqsrjlOlMYOhYAoATaiF4y6dOqtT+FlNDZB8DFq+0wJSamrjKehXUHWFF12AxNorWuiAnaHRa93adY4c/jsjLZ0MlZaiAhjfCycUc+D169fhDnF5+fKl+r5Xr17FGTN52Smfi3rLABcgOXOIOxft7Jmz1LNO3w24tLR0+tRpquxAoajTVm1AhPxXr3UnMkMlwaNHj+TEZSFVLqfSSLdD4AZJCYkvnj1XjJEtjUoFpgCYCQ5ntSpVqXOKkJvkdfD3ocMh0mNF5nJguDgGQFt/cHu7SVOes4MoAPXPUatds1bB63xdHiCDGuodxeFwnDp1Coe5k49VR5Xg4cOHCrMWUkdbt2xF1oXA08OAIGB8igUdeLtZ8+LiYj9U0kZq8jr4/b49e6umViHSYVnRfhky/UWThMub2YWBIRD8/fffsuUoynIuQGAFkxyBxFxhc2bNJkfnK2KKIYLBFADKgJWGRCL05vnTZ2TNWp6onkOdfQTTBAWg+3vdyAFqQ6cOHTmyIrImOuATAHDzIN0/UIuJit7y62bcVcb4jMEPP/xAzvNwNKQAuDyPthcvXOSZ/UaYD2gCGHPCBnMYvP/j+NiL5y/gjmGyhG/63b9/v1WLlvI5QCSkLwya/jG2SRMmKsjC1jtD4EBcCwfRwom0pbZmqf4GZOCtkpIKp2Eulf8PWxSVB0wBoAxysR0/egwrAFZz+3E34C/79uwNkY/8+++/REbzqBAKRbkZH+9RS0EQyNq/285Z7mAsz0ggao8fPz7c8xAUAJes1127chWJ3WTqT3wUbswujpVYpIQsWbTYq8uZLhuw14ugP4sK34z+ZBSRIZQ+rzCyoYH/uukXNaEYGALE1KlTyTM0S0n/NjL1JxrF8mWZTNxnYAqAKQCLcOuWLNo8QtlwNWKPZJq2aCirqc4vDlbVWjVqwpmGKwD+4isIacH8r3QcCCeXBCarJqN2/uy5MD5XBhmKadCqRctwT91Hjx7h25UUFbd4+x1FlVzDm7R2OnfsVF5ebvgTEICewtrVa2LtvHgSEg16tVRt0GZMJDS1hp7+7X9uuZiZk0Er+vXpy8vZ9Gkzk6CbO+VdjK1u3bpFRUXCkNgiqNxgCoCJsCJzOXU2wRN+R/IRp2fdIjnNv2zRdJfZwgrAzOkzgtpf1VsyYk810tL1GpGUaFJ0fMQ6AHrz2fhP9X+KDCoovFyKi4uRDBruaex2AXK65s2Zq62MtObJ5s09QFg1qckpD+7dpyt9njl1GqnokhovBkATtYGsZdT00sjKEqQLJaK8o6xcfTBC6ykwWA4N6tWHqYUTeVGf7YE3UgHYunUrm/kMLqYAmApTJn9OkUGQmcJ5lQSDpOcmjRp/t2Jl7Zq1JPO5TSH3u1OMHT96LERSIF1IkR4eAnZDYXy8Z0WF9Gppubm5LqOcsBkwbc+dOWvA3vn48WO43fmz5wyT/nlP/dljHtqEk/dfNm6iO8fg7s+ePG3X9l2b55KPSNcg/CB6du+B7Z1smTMEi6KiIhxBRPiRWqbhYiAd23dwOBy0yclgCjAFgD7wbvTh4CF0eQQI/Tg3C6kAjBg2vLCwEHXy5cuXw4cOI0JpPfJsCqXF4xNKi0sUQwtkx8XfyfkvO61qNV4W+klNQIMOgLO+YIEAXrds2ULel8kExgDR+acffrSHP8Tl1atX6HZo0jZt3IRQAo3wclFXOIb0/yOHjyDP3I2fcuRUf/PmzUcjRnqGQESUFxDJK9AY586eoyA7W/IMgePm9RtYYSa3Pws10FvOnj4DI2Lzn4EpADShWIHd3+tGnUfwcplA8pNhHw4tLy1z99np+m3rNpxYUFYYpNOATh06gpQTuGCNAwbEn7nGjBpNZkMKMdkCWQlV6qqN69enr/pBMIZoDMaOHmOAqwkoANOnTrOLiXd4dybssK8grzO2ft16ebkvadPeDYiNXrJoMUecAESGAqA4foE/d+/ezUR/Bs3I2rwF7yPgTWo5BQB1GPFeF5v/DDKYAmAKwIJs3bKVGTZOhe9NsyZN3xQUulRZMp8+ftK7Zy8pj4pgqpeiBb74fIqGseP3u3fugtK/POFNoXaoCIrr2chx2biUpOQnjx776gOD7lDoV2QZ5vA1pAAcO3IUtmpx/sTgTFDGNPI8LY6znzpxUk0Tw4jv9Y7wyY7ftifExUdMelCFByN+haBwpgMwaMPM6TNs8tQCo5K1FADU26qpVbKzs5m1iwGDKQAmQv269ejyCEVhJnhz8OBB6J5XAWLNqtWJ8QlQVwt8gdAniu9UOHD8nTu3biPpXKyXFE32CstS2hif4uh204aN/rvBoC9Iwubk5BgTPPfi2fMG9erjvN04hN3IdYQ12KULF1GkP4avcJeTJ09mpKVHUiIgxQlMjbR0X3RgYAgE3bq+hyeVRRWAn3/8CcbC1GAGAFMATASkoFPMwqH2/EGtS4eOZA8VwUPAPpDU3v7ddjgGYO8fe9TfCYTRPH36FElsYr5/tyASej0E0l8TvQ4ZNNjFOCA97N+/34AAAF5MRQXPnQxVN0ABUBxYofe9u/dwlXnk/aTl/e8Hd2/fadigXgQoAGTaAPy+f99+xtCZIVKRmpyCZ5cVYwDQHo2zD7NDAAYAUwDoA69DOTeiYEqP42y2mCjPbJth5xE2z5q7QtKSX37x33lAWVnZgq+/Qf1HVzh79qxiXH6GjPHsydPGDRtBYnI5K7k+gyKyFdlqVq/xMieXMT6DQapbX82br+/z9dUS+FhapTrJA4fq6RnPnj2jS38/IP36Xrx40aZNG7eIY4vGzMda2YGg87jP6M95c+bSpTODhaCWj+/cuk19VgfTlLV6kDhx9epVehRlMCmYAkATCkaDqwxCZm5YvfXr1oPFHG6uIUhL6J6i1gGSE+oDkgkCHozrwrnzzZo0vXXrVlBuPwhXL1+pW7uOGE8sFirXKiBil2syfRBOTopGd+Tw385ylgGNHpyuHt26G6MAxBKR30buvvIMlGbywT//ok30ioEXY0FBQb8+fd2V8kTpweDYiRAbJFxCDFPUW4RHj/7cvXMXXQozWAVeN6+d23dQn9jBNEFgiImSdAC0oc+fN0c9RmYIY2AKgIlAKAAcRNa+26btvj175Z047IwDNk7RBV/4s3nTZoF3XsotWFAIJQa9shgvjjdO16YNGyEGEUdqahYQfYh6wqXQ9b/56mvm+UMXZSWl8bFxsC2FezJTr2mFptz8ufNcDovtteWlZcM+HMpzdlmdjpK9+2gSM1jKwwkAFyVMM8RI796+Q5uuDNYDXrazZ86iPqsDnfyiuRASD4Ay3KRR45KiYvXQLMSUGMIEpgCYArAaZd9oadNqUK/+i2fP1/30M86KE+4mWc5E9oE20YH9BwQ7CnI46s8V7x/9+/D9vv3IXKK86LFNKiFBNWzxBU2ADB3u1aMn9oBkoAL06M+cOg2nTAacaPGG2/7JdYRat67vQfJcc260fnrlcDgmfjYBLyhjTmx0fOgC/4h+C/dZqAHMKh8xaAVaKSbJ0B1IU5hXYu386ZOnFMPx+p6hEoIpACaCXK9KqBiK1u2Fc+ddYiJzcM03hn3AiSG4A02Z/HmAPfd1pOjrkxcvXsycPiMhLh6bGAW5X3bdgdSi2vZ+nsjBgm3AdWrVZunP6ALI/u2SpZAwSpuCZ5WGxlizeo3nT58phm9C+OyYwzl/7jzsCmghBYCHyG9RDILOd+nU2ViiMkQC8H5RWlyCtirqszpQ5kOYV9D6nT1zlvrcm22FDACmANAHXocxUdGiBCyYD5cvy4TPh3041C5JyeFlHNjaBx7zaONEukfgnQ/km+Xl5Yf+Ojh29Jj42Dgii4KUoUV0NXbnWdMwBDL1Cn6fGJ9w6cJFzU+HQUf069NXUnEtJVAG1SBF4LEjR82/v1YgBzicSGHjPBPpWqJxMVIGBXgfuCGDgYFcFPB65tRpCyX8wed1qM8t32lRUlTMTP4MvsAUAPrAvAYKDaLV+3az5iUlJfC/XTt3EetYGeQyIdgPoqWNc+7sORV2W/Heq8kfPjx+/Hh6tTSyLi+nSDbCxXChuW57rQG0fft2rx1jMBilpaWpySmiW3YkKwBoDi9b+i1tYmuHYl2vXL7CWtI/T7gy8p7pzxkYAoFCMUZqMPWAosAbnKijaR/Hx16+eMnluTsz2z8DCaYAmAhJCYm8KBAfPngIf4iUASjSaQz7IE8AJk+cFOKIFIxm985dyYlJClcfrAAITUrcHhK3JX/+9dx5IQ6BQS+cOnVKDvaIimAFQIicsfT26lT+KYQhWUcA4sUAKkigDBxGUYOZgcE/FNtW3959qE/pYFo0KAArMpe7vJ3yMemfAYMpAJThXo0OZ0ZaOlrAnTp1chHKOvpQro3lY7fzLNyr/i/8CuJ17Zq1li/LbP9uO5x4B2f6w5Z4XvzmsA+Heu9qCLhx7XqjBg1lKV9LfQNyvKooTynlv7D9x9g++ugjxuxMAvQgFi1aJIaXSLOL9japz0brTrktGp6bN27y+vVrxcBp0VxH/PzzzzzUCSEO7uRyB9QfhFcuIdlBUZ/RE4mMp8BgJGDOlJeWpSQnmnCeQ5CbZDITA9/JepdIaQlc6Gero9KCKQA04bHwnC6QjHft2kWe2VVNrYLFpqC4A0+kwQHWkJqc8v3K7wTnIqcra/MWItZWcsEHBQD7znZo117d1RCZBfp5Xu7Lfn36ao705b3pAETG92jII9SnV+/S0tJQusqgL7p27oJV2QhQADwKDIsjSklK/ufGTRhsJO2pMBYpHZksZ8iBSWYUjMQ3Ah9ATKZOrdq06cdgPcCcdzgc58+eI4vKmafhTRBH6eDlWT0947/nLxRj8T9ShsoJpgDQBz6ka9u6TbUqVRViaxwfC0VtKmQEZIsltmdgCgM+6A9pSeB2xW+KGtSrz7mvAJ4/uPBwDAg0OH2evmyirKxs7uw5oVRTJ83/iqMAxAoRJQte5zPWZh4UFBQIkd9SWVkuAhQArDaDMxvafXft2Kk4cLfoDFTHQQKQDgAjxUQwsSuXpJi937efRZ8CAxWQOazRmxWZy/2fwNNqCl9Zd+SbjQMXYjbtGSoEUwAog1yl/fr0Hf3JKMWHcg7Qil2AyE+wbw8EA61dvcal4gg7fttuF2UXmXdES84z8qaO/uvu3buuMAg0cB0kMCUnJgXL+Mhk/2SpV7tsAmncsFH2i//07TBDiCDq2UXzVssq430DlqNlYDOeNWOmetSWnn5eO0/GBAP3oP4glPxBrqYimDNibDOmTTeedAxWB578fXr1NucJAD5Cxy6+sA8uXLjQ60CCHThDZQBTAEwBsDog6f+XjZsU/yVXAQvUAkHoA4I0n5GWfubUaZePhT186DC7mPJf/GEU9gLCtoTftm4jO+n1vYbBwhtHWfmojz/Rssd7sjwyeSiS/p89eYqNl4ydmQRfTvnCHfAdES5AEGMKMxCp7mgyu4gA2siYe16WvFMq5oDjAag/COVzkdTLaHhAmzZspEY+BmsCT/uCgoJYOw/nltQnto957hHj17d3n9DrXUYA42IIEEwBoA+83mbNmnXj2nXFpgs5rQNUAGTbv1S+p0G9+ndv3/Fz68L8gtYtW8m2BHdEI/AU9Pm4MWNdKo6gmUGQP7x4/kK7tu+GUvALDxlOA9ClmjRqjKR/8l6Ml5kCYnwLpLgV8sxSKtAbpj24aeMmr17muSrPlBNrhHGEDdJUjZPrAICqqSiDysDgH+T63fvHHvLc0lSNcx92CQ29r1+33sucXHIsQRXAhi9HPvtiIMAUAGpQr7QVK1YUFRW5PCWJlKTkAC0QitDYurXrPHn02P8dEbJf/NeqRUsoNQAxAFIlYHF3r1Wjpu5izbUrVwcPHBQTFR1KAADWc7AjUOOGjR7cu8/4lwlx785dXGYOxDJrpZX0swdXSUm9ffs2bQLrCT/aPvl+0oSJChHEJE0KhZRnWm52jnG0Y4gI4OO7CZ9+ZloFABzwcJKPpITEq5evhDheeP/kyZMjR47oRk0GE4MpACbC/v37yT9hTVZNrVIhA1JHxNZIS7937576al7xOu/VwP4D8AkAdgECEefq1at6DfDkyZNI9AcTHWTs0cZYyfHCmzYtWj59/AR7YDA1wFRY/f2qWLnIHYhl1LdPfRpn/+vAn+RII2PiBRLzU15ePnL4CPqPQNWkrIjiTEuvlmbtmgwM9IAmf+2atUyrALjzcYmmup3bd+gy5KdPnzZp0iQnh6nNlQJMAaAPvMu+ePECf4I/rF+3nv9CYASHkhJfaDMGbFi3vlqVquSxPghtQj1gz000KI8g9L/Fb4o2rt/QtmUruKzfRP4e48KpzUiDMQxWPK+QErB0bN+BMSyzgZwVPbv3gHlF5q6mvoMG3nB2LGhSuHx0zMrlKyhSmDpKS0v79u2Lq4jY0UOVUgjQlJZwBxBzeK9LV9pEYrAqrl69ivPv0Q12J+1xvGzzkpzcbNFoi5wv17sMRNv1daSPPnlw737jho169eipMykZzAqmANBEIGG1FVYCxkFvkpgSY9u9c5em3rjycl8iVoLUACLVt5BI21leQTJQr5+jDy+cOz9pwsQqKak4X4HCb8ffRi6fcnKeTFAQNWSHKPS/fXr1Lnidr2W8DHrD66aCJpUQSEfkrbOcAiAsLi7Ghk8wxPbpuPGw31YW139vKCgoaPduG8IAYZMSCtNzDcIKAHpi48eOc1XWR8MQIpYuXmKqrGWqHVOS/ocMGixlIAhsnvv62tXLV2pWr8Fz9h/WrGVLppKAKQD04X+xde3cxb8C4N75RD41feq0kHridJUWl+z5/Y/hQ4cJ3kfiZY/+fcTjO95UF/LU4v79+5mZmc2bNgPxXfb2sSnkfv9qAC5rgnOtyMcF0Tjz+mfjPy0rKa2QhgwGwJffyLasrZL5XxYKraUA4NJ4MANhLD26dYd6HVZP+R8s1MPMzs6GCoYkxSjWB8C1JmKiolcuX1FJnguD7ujYvgNUALDTDnaHg1M7sV1C2m7Eizq0a//mzZsAR4T3aPWi2L93X1JCorDDcnaoF8RQGcAUAMogF6TXvWrokA8DUQBAUG7VomVJUbFee57D4bh58+bunbvOnDkTyGHF48ePly9f/m6btlh2hyNUKEkGXIwnS5YE4ALEyd8nVQXgyCtWrKiwSwxGwqs0LE1gzo4ft+UUACzOwsRr2rhJXl4eHiCZaiOy56GCTeE39+7dy0hL5/EJD1UFALttCGehu3dH9hNhCAfQnHn070Ocfy+Wdr0L7Oxq97SFNazfACreBDLJfTnuIva14OtvbHIyvX59+gZ4QYYIAFMATAE/6w0SEfgvBAbcIY6PvXzxkl59CDCVPvrff+8/WPXd9506dOQ8/fvJqGJsQ+W9HGUGxAGxUoEGm5yY9Pvu/1VIOgbjoXgcRUVFKUnJkDYeP01zpo/01RQKQFrVanfu3HEFoLpHHrx7+omv58+eS0pIVFOMyvPCVlsdExgwVCqsyFwuCNxcDPW6JdgDFvY+HBpXrUrVWzf/CeoEUvFl9Prs2bNePXqSDns7ftse4NUYIgBMAaAP/2t43py5omDtMw0ozoI/Y9p0vdatLz8fBV68eDFu3Dies6vc9N3++lLqdy6G9xT9/Zv/1UwQ1x5q1qTpPzdu6jJMhnCAVBr37t1rkw+vYaLizYzWhhpsg2A7UEGRjn3y+AlypIqBG0poY1HhYHft2InP/UiF3+CGM02hiff69WsDKcQQKXC63m3TVjCriWvfJAEAsbIJDLXE+ISzp8+QMUiuIPkP+vLeP/ZUT88go7NSk1OKiooim48xkGAKAGVUuNjW/fRzhS5A6As1Mqrn5+cHeE0du4eQk5Ozc/uOT8eNb1CvPvbbsbvTkgoFhgUdwNP/JxDpnyzxC3xq5PARhYWFiu4xhmUeKJ7F+PHjPSZqVLTiyVqhieH14vTzmmuv0k4/9epbtvRbQVEXColQe75YAUDCjasSPx0Gzbh35y45l8xT7AI21lg7j7MPa7NBvMzJHTt6DCn68+KVEbvWn5oMJgZTAOjD/6Ldt2evePbnb0NFX0B6QoWXCrYbXiN9/V/k0aNHW7dunTRhYou33yF1APCk5IOX/HDemPjYuB/WrPWV54xt8iZEeXl5RkYG+RwDP/MxU5PS2ixflgnj8rMKKs889JX4SxAsqGYCxVbbzh07GU8WhgjA0sVLpI3VZHVLQGTfuiVLnX8swK3f4XBs3rw5Iy0dJ/rDBXnQn+fOnQsnXRlMB6YAmB2XL14iw/9xYhyOYApNGjVGwhYtW5fSCCG/2fvHHsE7yJ093SdTg+FgM7/scxkN6RfR+9YtWzF3XqsAz4fTJ09ZytJvw8nsJd9fwplk8sRJdKlqWig8EEpLS7t27mKTQ4AgBwBJzPA/R0lhG/3JqEqkkDEED+9B7U5Xy3daKCaqAfOWzI7NeVhMpLo3sC2uW7fOz1j8jA7hzq3bPbp151T3hTfvtmnr52oMEQmmAJgaaB3m5OSoAyg5IjMxev/Lxk34+wZ3z9efWZu3SNnfA1AAcGCTxyfge23j5s2ZW1ZWZvzoGIKCer+ZNGGiwRJ8iE3YZaWJGo3FVjQzBw8cxGagf5CU+e/5i3p16kLwDyKpUCZMdKeG2NzwC1JRYM78ev5XFAnCYAmoV/Stm/9wqsT/BoQBxCqzfOJP3NL/mlWr1YG8/ocDH758+XL61GnxsXEQSkeexOJgvM2//OrrCgyRCqYAmBqwGqukpGKmYJeN5fhYoHbNWuWlZYqfUITD4Vj4zQJOTuSPEwH55n24sm8U9rcWfmuLbtak6emTp1xO+oNiCApoDiCJGVJDWqVBsAq5KcIq69Kpc1FRER4am4pqKGniFM4tE+MT7ITXH7Zlhv9RSjxk4/oNlOjBYCUoZu+cWbPJA3acudg4RuR2nYWFIxUByPx2WeCjwB+WFpd8t2IlLu6pSNeBb5RWtVpJUbHOlGUwPZgCYGrAqm7X9l3yfNAu5wKD9fztt98qvm98D/H7goKCj0aMJKOLcBCwnw1bsHBEvSWKX9EwuoS4+IULFxYXF1MZFEMogOf114E/OUsF+0LRH3JTRFPxneZvkyn/FWNkwFATZPu238j8iXa5Wnn4H6Xk0Hz44CFffWNgcHmbGGVlZTWr15BNV+TUDTv/UWRIw/Y+qehN5nI/nfc6w9FYtvy6uWH9BlDCXH07rJmjL8yeOSuQUj8MEQamAJgO6kO9j0d+BEVApFxgUVJxcsj9n52d7f8KhuHu3bstWrQgavdGY6M+pAH1LngR34FBvd+33/279xQXZwl/rIVRH39iCybTK/WGfVRwxr26tes8ffwEhsN2xwDhJo7TNXP6DE9vCoNOACDY4Pbt2y72sBgCA8yTA/v24xlLFqw0gv8QKe94OUIXXnH6AUVvfWXrLi0t3bx5c+OGjeSx+Ft3YOn49/4DxUUYKgOYAmABfDVvvuJEkpeP80YMG+7Smgk4dJCb/R//+71KSqrkuE8UAAa7vh8GBOcDMBzEs/63a7f3WzDGZB28KShMTkyykPTPi8EqUrUK0SSWXi3t1s1/1ENjiqhXqM0WCOWlZd3f68aJ6V9FcSrKgAJhUvBGjI103GJgUMDLEna6Bg8chMVinhD9DdABSJO8nUj5/+PaH1w+ZH2vJxi/bvqlSaPGOLIO+9Z6rpEY0qdg0ICBCrIw/lZJwBQACwAO03lFnJDII/bt2Uu7d67CwsJJEyZC2C5OnyIL/VAD2J8CIPzKxlVPz/hhzdrS0lLyyrQUG4YQsXP7DoqivEbBkXMroqnJKRfPX8DDYZtiUCAJ9eLZ89o1a0E0haIieNiacJcaGdUpUoDBElDI008fP+E5O+9ZpNIwJ0bypriWDmT3VvTWKyN6nfdq5fIVaK258wTKu7CsA0j+RYrCxuhDIdDO95UZIhhMATA70Jq8duWqOguBTQzcIcN/XTQW8Pnz5yV7g8xucOlfKSG3VBXIpwKQkpS8aMHCwvwCj4HQGxGDNpD706ABA2nJ8ZobTFc0bZMSEo8dOSqNhUn/gcEPlU6dOBnHx4JEYsAJALCgdm3fVfSNgcE/vp7/lWfIivu9MWoAtsqjbqAls3vnLpfDe6of8u/79+/PnD4DbaNk+mychJd4jcEHC1icQLd7r1NnMluuw+EwhtoMZgBTAMwIZWrt4hJsmcCbKFrGY0ePMb5XuG9v3ryZO3sO5PqUDxmjFdwH8iriMwF8FglsCCkwC79ZkJuby/x8IgnogcbF2sWn7xFLZ5rmMUVxuk/4BO27ZjhVixjAcl65fIXgYSUn6FQEB+suXaGLDx44iPbQGSwAvKmVlZRWT88wuIAdOfnJWhnJiUl/HzoM0r/XDgtwOA8fPDSw/wCh2E7AeYpIlQAOPPfu3eti225lBVMArACHs1mTpphNwDE6UuLDIan4P2fE7PLw4cONGjSUopTc+T2k7OlSNSU5DIAQtqSTzbq166xZtfpNQSFL8Rl5WPfTz2gOQPY6cyoAcCYu5/0ktFbO/tvWbS62HeoBDxo6Xe/37QdaFrg3gKXTHoYwcZh1kyZM9N4TBgZv2LolSzafh50Fka68ivmP+oD0kAvnzvvpavaL/1ZkLm/auIln3cwAvZXcUXnoV283a+5no2eIeDAFwHTwGpw0cvgIWMC4PFYcH1tQUODtAvrd19sXHj98hDrDKTmaqn6qJAJGkewGdbtzx06I1WLPJeZfEXl4t01b2aBuaP7sgJtb4ici5IT5iT1u2YTUHbnZOUjtBwMn1DDBYpBNb0URXXDJosVwX/YoGbxCMTFatWgp6qXhL1QnT35eltrJrNlIrL9796560qJPHA7Hob8ODh3yIdr6eaJ6ZjDSvw1b5WAl7ty+w8V24UoMpgBYA9+tWAlrHlcn7f5eN93v4r+4INI35s+dlxifIHv8K5hONCgnWK6K44QPY6LfQh9WrZIy9Ysvb1y7rr4+Q8QAPdmrl6+QZV8NqKAZbJPKTcgHVrGyX+y6detcgVXWZAgEar++E8eO85wdnwDA4yAqBuqpANAqjs5gIeD97ujfRzgxDYABdSpwEU+pyVV9UOvSqXNubq7L5VJM2ZvXb8ydPQcCfL0W8Q2wid93lxZ+p/nbznIHSQoFZRgiHkwBsADQajx27BgZ4oPeLF64yID7wps3b96sXLkyvVoaJ9UpdLv6qKP6sG1PEP44e59evbf8uhldweuVGSIMSM3DamrgnqlGNuJUyl1Xm8y0zSZnmIAIu2jRIsiyiicGyEP6Koro4of+Okjel+KoGcwMmBt9e/ch8liEl/8AV0RbpOQFJy+Hj0aMVCSufXDvPuJLbVq3BJdaMq4XHCx5z3phFfA96eRBUHJg0e3asZNF31VyMAXAGnj16pVCAThy5EhYVyxcvKCgYNV334PtQTbUyT79cp4f8jSAk3lc75691v+8LpAiZYzvRAxKi0uqplYRdiNwBjNpJeBo0vaP9sIVmcvZIXiYoCBpeXl5546dsBGUlwuR6qsooqtdu3KV1pAZrIXzZ88Js5EzKEsVaRbBHvyLFizEK+Xx48crV67s0K69XbLuk7V0JKMbxAHi4sGBFCzDgTdxYtBwi7ff8Zrwh/HASgWmAFgGTRs3iYlyRyvqGwCgxvOnz+bNmYvkOZyTmMwrDE6Ebh1A/EKVlNTBAwdtWLc+5z+l3O8KoKQXYz1Wx7asrdIGI26ldpMWAsNR6THYWdxr7DubkOHAgwcPUpKSSbcf3U+K0NX+e/4CbsceIoN/DBk0GFuyjFQAwPyfnJj0x/9+dzldt/+5tXL5io7tO3ByLUL4PhlKJxjv5VKbvHyRCkV/aPiCcOwJGUTIamKs6k4lBFMATA3SMCmH3gpcoFWLlmG6oxBpdOjQh4OHIB1DZkPRiiyf5J8JvL1rl04Lv1lw4tjxkpISP6NwMUfDSEePbt2lzQZKQJjR/G/DE7hCPzqvtTYZAocv6mVt3oKt/uFIBISujJ2bGRj84MqVK1JYHXGaHdaG0/CjmV+7Zq0f1qydNWNmkyZNSJUYEutJZhT5aALH70oGOM8UQIHcF7/v1KEjLBDG3So5mAJgGXy3YqVNTpoxZtRoXa5J7tA3r9+YN2duvTp14YCe92BG0j4NzKt+3XpIQ8jMzDx16hQ49zM5qbJBrdQ9vHdf7YpK1QXIXZaOLE4nKbE2Dvv9s9lrJIDaI4YNl2yZsjKmo4BVPT3D5Wk9YWDwikEDBiqKZ9HjV+FtZJ2BY0eOSquDNv0Z6IIpAJbBmVOn7XKZwO9Xfhfgr3wZ3eFNSUnJiWPHkdzfrElTbE4Awz9OMVYjo3r397pN+PSzNatWH/37SF7uS197KttrKwm8HunMnz1Hd1NuKA1nu8NbO85SRUb9sklrPBDNc7NzatbIwPUi9BR0YmyIm5H3ojhSBjPA1xH0uXPn8Ck34d1Kn3eFp0n5Awd80J/GQ2AwI5gCYBmUFpfEx8aBUP7n/gOB/1DhyZCXl3f48OHMzMw+vXonJybF2vm0qtWaNm7SsX0HxBrGjx331bz5G9at3793341r1wvzlZEGXp2k2S5bqaCeA0VFRdWrpeETABydRnfDUyT7x/EqZM4fF5u9xgJTe+8fexSHjbo09HzDkSKZwepQLnOn4LIou6KRfq3UJfWwNEh7wHP2f/75xws1GColmAJgHThdXTt3Adv8v/cfBPdTebW/fPnyXxEPHz589eqV4LXvdEnN85ssTpfBDxQz4ZdffsHO3LTlflkQtOH601G4KhlaO6tWrfI6BAbjMerjT2wBZDAM7rnH2IYPHUZ7ZAzmgnqx/7n/AOF2L1kKILtOpDa00KZM/pzxPQYMpgBYBmjdzp87DzGsOD62tLQ0qB8q3oTSB8WrLpdlsBC8Pu7WLVsJISJRbvsZdWVALusTDT0BY/OmDRsDGQ6DMcjLfVm7Zi0h34B+8wRxyM8nTaY9MgZTo6ysDLxebdKJpTvolrqYHr5WJSX1ZY5UaIxlOGBwMQXAWjiwbz/iWY0aNNTwW6/BAF4zAfv/LQutY1Dg5PETZBAnddFfEgSlbNnRsM3Hx8bt+G072W02h+kC6L/3jz265wD95quvaQ+OwVzwWOxO149rf5AC3tzJMXHIEH1JPSz8MMa26rvv2fbNQIIpAFbCq5d5PGcP3MPVfwQwfq+ZHTA+woAwdMiHIMMpDgHo6gA4iR6S/hPi4vfv3ednurKZTBHDhw7TMRIAPe41q1bTHhODiaCQenP+y4aq9nI6zkh2+8GtRYsWZWVlarIwVGYwBcBScLrav9vu45EfBfp1PVY4YxMMJBTz4fHjx3F8rMKIS938z4u1CCDvZ2pyyrEjR6XO06AYw//Zuw/4KMr8f+Dced55p7/z7rz7X5Ek2wIkoReRJkWlFxEJTaSDSm/SlI5UAaUqAooiFpqA0kSkCoqgiID0FkKRhCSkbjb7f3af3SdPZmZnZ5PdnZ2Zz/s1r7w2m93ZmdnJzPf71CIKhKfQ7Zu3KpavEKzvnXznn378iVo7B5Fv9CujzO4mgtwQYZ6TJ6LGMQvucujQIScq8KEoJAAaM2vWrKlTpzrxPwzqKTL6p7tfioqjZ5i9sT6txCc/WVtei8lctXKVn376ScVjBUp88sknbLYjNvWp66ss1vmwfes2tXcIItTRIz/Q0bRVj8hDthSZ/4RWdAwZNFjtAw+RCAmAZtCoa//+/YsWLUL0D2rhz73MzEwSYZtVrUY3ewf84W9+9M5Xt3adSxcu4p8lAokHEujUMZF127C5QrRiThBG3nLo4Leq7hxEKLvd3uTpxsHtcxJpC+3Q7LkSuv8dKiSUv3XjptrHHiIREgAtIbfJnJycdevWsV/V3R4wJtZvZOXKld656FWrAfAW/BcOQko3qWnjJjeuJ6t9qECpc2fOxlptdGImm6eMtjgTM5Gv/tdTpyWnKwGDe3PefH1H/9bC0hDP6Gdkf1evXq32gYcIhQRAY8jNLDU1lf9VxY0BA2KnXH5+fq2aj7uG1zTHqJgAeEu8CuM/sknt2z2XkZGh7oECX6QHJyjwNCejOYC3Wing84q88fq1JHV2DCLYmdO/0gzTGhmdlEK0eBtDen7t0D4RQQL4ggRAkzCIL6hu/dp13pl0olUdSYM2HHeVGdPA8aV+L2ZnZok3GP8ykUOyM2JmZma9OnVN3jqc4s3MSt6YkZau0m5BxKHnmD03r2XzFvR6xY9Upr/F1ZHG25W5bGyZi+cvODH4AfiABEBjEMRAJCjIdzRr0tRU2E5D/RoAWng8dfIUsm1FNhXZckQSjEpMf+7cvoN+jyVJABz2fDV3DCLP3DlvCAb+1+ti9taCkn+ixQsXOREzgG9IALQK/9WgCnri7d71tbucyRX9m6NKq5gAsMEurGbLeytWssIuX5NgQOSQ/I56du9RkhqAsrFlBAWe+OoN7ofvj5CLA+1fzrrGqh6ph2hxZThkZ6NjWrdslZ+PTBjkIAEAgIB1aJ9IB21kI06odcOjY/7ElS23Y9t2VHVrjjg6v3j+gs1ipZVLxWhaVqVSZacDPYDBIy31bt3adWg+6Rlb1lSc0aW0stjcXWjKlSl77tw5tY89RDokAADgh2BMlWM/HFWpFM0zvjWdxIeNFFmjWvWfjv0oubWgLfS7e33qNNqznCYArNmGpXCAIJ8nCYn21N4JiBQOh+PF3n1oWKyzxj9mb6NHV58ZU+HA/1Z3+x9XXSiAP0gAAMAnyWC6W9cXaE9NeiviH4chATB7S/3pRL8tmjVPunot/EcGgo6dbBkZGdWqVCVfsXgYUL85QNPGTdTdC1AdO5GWL1+ueqQeooVedWlbf74CljzTpVNnVIKBEkgAAEAa302TPT565Ada8iQoiwpD9M/Kg210NI8YU9/efeiAPxj3XR/Yd/fJmo/5+SVoxM9+2rjTT7A807qNursAquC7ktOfhw5+G2u1selBbPoaANQz3aGrIrRwj8g+VqpQMTnpOnpAgRJIAADAJ/Gdo/sL3WjJE5toJjwJAC0PZnc7sgFz5swRbx5udfrADTNlsipr/EOXjokd1N52UIFgUCkSBNeoVp1F/PTkIT9lUkdtLWzabEE/mc2bNwsOCIAvSAAAQA5/Izly5IigwQ83anvIb3i0PJjcyOPKltu08XMVjwmEgiBk2bdnL1/RpDCA69Gtu3hVYASs+D8rK6t1y1bi8f75eiStL7QTlM0Uw4pFyKV43Jix4gMC4AsSAACQJrx/OAo6dehIw32+ODZcfQA8HQDq1Kr980/HVTokECqSjbi6dOkiaPxj9dcEqG/vPr5WC/rGppR+qd+LJk+VURQL/ek5o5vewLRAhOwg3Ueyv82aNM3NzpE+JgBSkAAAgATxXK2HDn4btvJ+XwlAx8QOKSkpfjcVtz0tEn9rJ0+eZI03yE/50J8u/V96WWaFoHMFzlkzZnpPGM+oOGyhp1Cs2aKDHIAW/7OZDRLi4s+flR73EzMhgi9IAABAmuC24Rr7P4yN/sUJwNQpk+gsvw7fA/7jVqczI4cOY6GbkrhtxLDh7L04GYyDftfvrVjpLeOP9raSF4fOmo/+6fWQTcNCrsYb1q1X+xsA7UECAAD+ff3VLrOn3jk4NzDBbZhVzdNKBtb42+Ie5y6+XNyGDRvolgh6+4G+Xb9+3Wax0oiHRnXegk/p82rkyJFqbzKEUOH/vEPYQPHLLV9YzRbaQFHt6DxoC6t0NRe9ZrKx/8kLXh07TnhwABRAAgAAfjgcriFZBANOl3yxFe3iSavmzd7bnud5k7lRg4anT56iW4Kg34AmvDaehj406JGfeXrSpEmCt+Oc0RNxez9q/959sVYbXzmpj5J+Mzfnl5UrKHG1/ifPRse0a/tsXk6uGl8FaB4SAADwY91na71DfwbtnioopWMNPFi3NjrJ5aABA7PuZdKiLV/3ftC3WzdulitT1jP5g4m1fJA+ryZPnoxB0I3m4P4D8eXiaKBsUtZUTCsLXwPALpXuB9HkT49Vr3Hjxg0nGvpDsSABAACfyE0lJyenTq3a3uL/YDYBkqyppwPbkbtdrNX20Yer+S3hf+JuZygzXp9u9gZD7nEPJdp202XixInsXThJ9I1+v8d+OFohoTw/PgFrLaPScAXBXCzuvNfEzWbA/kSukEeP/OBEq0goLiQAACDn3XeWecvV5EpeA134mxl/VzPFuMbueKJuvRPHf3ZK3dJwkzOglJSUhLh4c+G8Tj6LeKdOnSrOD3HO6IagGvDQoUM0+ucn+tVB3F8kB+BmwbN4J0Eny5rVHwkOC0BAkAAAgE/pd9OqVKpscY+pJ9/5siS3N35gPvJZA17un5GW7uuWhoZAxrRw4UIW2Jl8t/GYN2+e2lsKocWuAAf3HygbW0bQN8kSpmlJVMgByGlvcg+KQH5OmTSZbxgJUAxIAADAJ3Kbcd9No2m7iyCOAsQWviFQhbj4tZ9+5pQa1B9VAUZGvuvc3Nwn6tbzG+HNnz9f7Y2FkCPnw1c7dsaXi3OfD0VqJrlusv5njdDQ4prY2D3Gkdk9211+fj4uiVBCSAAAQNr58+ddA2uYuAGnQzMMKL1VP9Oy1eXzFxRuG251RkO+8Z3bd9DwTiYBoDUAOD307bNPPrV6qg0LR4Wi4b5ZL+P/iBfaz6F1y1bZmVn0OKA6FEoCCQAASOvZvYeJm4TVWvw7azQdwJGNXU2TCppXuJpzmC3Tp72el5en9h5HIsm7uwFv+XSXn+/chVZJ+TrZBDUABjxQOiP8BgucixYsFMxFqJuIX1xrQa+ZtNe7OSq6bu06N2/eVOmrAL1BAgAALoIb7d5v9njm5Crawa5YdzVXxG9ixXUWk3uGTs8clnVq1T6wb79aex3hJEv4jFz1/8vPJ1w9UrzNPARTSVjRB0BHxGd1Xl7eKyNGso6wOo7+WbELm+2L7G+FhPJnfz2jyncBuoQEAACE7HZ746ee5qef9OQAxWlTG+2etT6Gn+rLdQs3uaL/4UOH3UvPcBYYKIRVTn60U4fDEc6NUVfhcShwDhk0WDDqCz+iFEsAMFas1gm+wdQ7KV06dWbRPz9xuA56/RYZ6qdIShNNL7xlbLGHvz2k4tcB+oMEAABc+MGkVy5fwd9WWcPrYtxovcMHee7ZFu9YFtWrVtu5fQebvB6xmoDPA+IoSEtL++STT5KSksK7RaoRHIqrl6/EWm00YGIlpqz0dP78+RgGVH9Onz7dsH4Ds/e7tnIXE7PsqFBaWfi6LMFZ7dprs2X79u0Y7x+CCwkAgKEJ7yWOgls3bpaPT7B6Z+dlhazmEtQAWL0z+9Is4qV+L965c8f/xgCHHJx79+4tXrioU6dOfDtgfR80iVC+wDluzFhWAyBoM803AdL3kdE3vvHb1q1bi84CYaZFEux6UqzrUiQuku0tyQ5+suZjwZEBKDkkAABGV6RgqcA5aMBANtoPXzFd3JKtGNrWny7VqlTd/Pkmp9QtzFBtWvwS3OZzs3OWLFpctXKVrl27kjRA/sW6l5SUVM4Wa46KFkdLdCIwtTcQgsNut0+dPIXVPbLQn034xeaHVj12L/kirs6i+7hy+QonBvyBEEACAAAu9NZyYN9+d/QfLZiml916Ay/Tco32Q2/VQwYNTk1NxbA2ypHDkpeTSyKAGtWqkwM4sP8AOlaSeJ4E3RPs8qujx/A9Jtn5NmnSJPHrQYuuX7/+3LPtaIdvFvcLisb1NNi/5NV10aJF4iODcxuCAgkAAHjuKCS4fLJRA4s5xj3zl1lwuy1usZZrbbUfr7V719eSBf+4mfnicDg2rFtfr05dWsb56thxBfmeShIUB16/crWMxSqOnCZPnuw09pHRhy1btlQsX8FctBKS9QHQTdAvTgD4k3n29BmCw4ITG4IICQAAuJBby8K3FrhvPNEy46yzCgE6KT3XMziaX2jcT36azeZXX301IyOD/yAVdzNi8TE9+bl/354WzZvSqhhyhKdMmkyedEimUEY1eOAg7zlJ56h2LZMnumoAcI5FviJVOs7CMzvzXvorI4erHo6HdBEnMHS8f/eE656OvzNmCKN/gOBCAgBgFDJDo5DH58+fL2OLpaG8zIy/rNU1a9bv/VNh9E9H/Sd/atGs+bFjx8SfaMz4TKbdDh/9//LLL67prkyew0iyrJnTZyDyF/v11GnP2UhOtZgod/AU9dq4V9XeLvCJ9vMR5Lr8Cw4d/LZunVpBnHE80hZfDSk9E355i1Rmisr+AYIOCQCAgchMLNW+3XPe+1OUuxWQ0luat0beE/3TmLVCQvmVy1fk59n5jwDKVwOe5KTrQwYNpkEAPZLkwawZMwVvBI8CZ9cuz9PgiZyxdMDZEcOGYwzQiCUzOcO99IxJEya6rh7udE71SD1EC2vRxGcC7l9dj2OiXOMlLHxrgRPnLYQeEgAAQ6O3mdUffMhuRXTuSflCLPFdzT1ZfTQdlGNg/wHJycmCjzBgv1UZgiOQnZn15rz55cqUpYOlegoFi0b/wKMH8KsdOz3Dy3p6rkcNHjhI7U0DpdiVYc/ub+rUqk0vLOTio+MaAKtUW3820Qo5mSV7/QKEAhIAAAMRxOL0wfVrSRUSyheG8gpK4MT9g+nMvk81enL/3n0yn47QX6jAuWXT5rq163gL/j0LCWonTZjoROLkAz0UDoejXp26tMKEpqB9e/fBRGCRSbLi6+bNmwP7DzBx1xNy/dF3AsCnAVZ+9E+T+aMPV8scK4DgQgIAYFDs7tKzew92QzK7S+BofbTMrYvdtEze6Xgqlq+w4t3lDns+v3JBpoH7mbPoQTh54pcO7RPN3lnS2OElz4x+ZZTTgdDfv2XLlrFu6OQgkpOZ/QkHLZLZ7fbly94tH59gFhaH6zkB8DV+UazV5pogBSCMkAAAGIVkWfKnn6yho094GvC4yv7l7r78rZqG/jaL9dWx41JSUpyyZfyIxvgjkJZ6lxw0Qak/m265b+8++fn5OGJK3L17N65sOfc8Fa6aq46JHVDRFLE830uBc+83exo/9TStuinSfchMGxOqH6mHbSFX1PLl4nbv3l3kEAGEHhIAAMNh95ikpKTyCXGFQ/fERPkt/rdy/dhIqNqpQ8fTJ0/5Wj//KyoBnN4Eaf369TWqVae9pbkAyDOqUof2iXk5uQVF36XaFmvB2NFjrN7uK889207tzQE5Z38907N7D24MscKhw+giXwChp4U2pHysarUTP/6k9tcCRoQEAMCgCvIdz3fuYhWO5ulZPMNQeioEuCE+aTOh6JgmTzfeuXOn2jsRuSSj9nNnzpJjzg+iSg+m1Vu10rxps/T0dET8fvGH6OjRo7Qyih5ADJkaHoHW9V27cnXEsOE29/RtJZ9hUBMLG9bT7K0v9XZx9ky13rB+g0uXLoXyWwLwCQkAgFEImkaseu99dluiLU/oTYu2UmUzKwnKp8nLatV8/NOPP8EQn0qwg5OTkzNn1uyysWVYuM+OPBsQsE6t2rdv3sLxVIg/UE82bEQTgAZP1Of7TkBIKeyhfuvWrfGvvkZCf1bW4Gs4fJ0t9N+cLrSfDxtigdb1pfx2R/7QAYQOEgAAY6E3mzOnf40rW46P/kWVAJ7Q38K9pkJC+aWLl+RkZeOOJUOQaDkcjsOHDzes38Db5sc7DiB3zMmDhLj4U7+cVHGzNYfNKkW8vWQp7UtavWo1VgOAszSkfM3rxzf2S72TMnP6DJL38vMGiscQ099iEo3y6e3+66lEHTZkaG5ubvi/NQAGCQCA4eTl5DZr0tTs7XXKRqHhR6ig0yqxF5QrU3bG69Pv3r1L14DQyhdBmei9e/dee+01VtNi5XpQsGdci9lC21PhwAaKHrGbyTfIMSTHM75cHJoAqYidwDdu3Jg+7XU6yA8b1Uf3cT9bWOUe22sLN7/HkkWLDT4tOkQCJAAARuG50zgKXp86zRP3cw1RWHjq/RlN/0pC/8kTJ926dcuJgfwVo0dp3569dIB/U9G4h+YA7JiTvy57+x3Be0E5WhXQ/YVuNJWlY9HiMIaBdEeXc+dGjhwZa7XRpi90igZ+7GBL0bIGXS7ijlW0xi8hLv7rr3Z5KklwRQVVIQEAMAR2myFRqaDIn9bIs4F9YqKi6b2qbGyZKZMm37pxU7wqDOkjLz09fcyo0Z62v+7oX9Domc26QBbySvouFAqWxPq162hNS0ZauhPHMMQkhvYqcP549Fi/Pn35Du42k2eOcKsxWv4IEgD2v08uquTMfLJho/NnzwmOIYBakAAAGAW539xMvlGtamUS+5O7splrqOoqkI6KpokBWUjoP+G18bdv3pJZleCnkQkC94P7D9Sq+Tjf39HKdbYWtA1o165dXl6eeFUgT3ygSNxfxhZLju31a0mqbJJh5eXkkuzr2WfaFhnxhhtFwJ0GxND/Alq4oPtKAHoQ6HWV/u+/2LdfRkYGJvaGyIEEAMAo7HZ7x8QO7MbsGuhTdNOqkFB+1oyZLPTHLUoJlgiRUH7alKkWLrIxi0J/ft7fmtWq37p1S5xN4aAXR4GzV4+e5PCe/fWM2ptiFNeuXZsxY0bVylXYeU77DnlL/aMFYwmw/wWr3vsDmLkcwGq2vL1kqeS06LjAgoqQAAAYQ4Hzjdlz2Gw7dIx/mgPQpUa16osWLUpLS1N7QyMXbWvOK/A25CWPfz19skWTxjIBgbcxdBQdCcRmsX536LAa+6Fb69atIyHX0SM/sGcQYAlIjtgj+Sfxy9gLyD/C/m/29OvVO9ZsUT3UjoAlOia6NB3f0zuTcWEhy+OP1fz+8Hd+jzBA+CEBANAh8Z1m966vvcNxCCf2qlen7oerPsjOzFJlUzWKL88jli97t0ysVWYSU29li6dFBPl16eIlKOoPCvZdkPQ11mojp7q62xPhlHQ1YRG/4Dy/dOHirBkzaz9ei57VgvFtjLp4alNp00p+YuMe3brTkf4BIhASAAC9Ed7XHQVXLl2uUqG8OCTt0D5x+9ZtDns+iqZkSJaSsggp9U5K9xe60UbPbLhDyYXFSeTF3bq+gPmqSk781XTq0HHDuvXsGZzYkvzmAILnSWa1+oMP27V91lx0hHtfZ7hxFhr0e0ZQ8Pbsd3WjKmN7951lrv9xnIAQqZAAAOgQf//OupfZ7OnG7qIpT/RZxhY7fOiwX34+4evmhLBJkrjZ7pHvvq9V83Fvj0a5BICNs0Tig5o1Hrtz+zf19kNXBF8KibqWL18ufgFQASVF2dnZW7/4csDL/ekM1nSIMHGgb6jhfSQSAHPhvtPmlI2fevrkyZNO2QZXAKpDAgCgN0VuMwXOPr16W7090mpUqz5/7rzbt2/7fyO4CQImvuh08cJFNouViwaiZZoAsfiAfAu7d+9WZ2f0RfKrOX/+/Lx585wKCrkNy2+pf9a9zG1fbh3Yf0BCXDzfXJC2cRfUdOl+PB+/CQA5LPw/+OSJk+gUvxjwByIcEgAAPZszazYtlOra5flt27bZ7Xbak1Xm5oR7lZgg1ky9k9Kt6wv8UN/uSChKPgGgbQMmjp8gXiGUhOBILl68GMfWL8EVgMjJyfnyyy9J3F8+PoGW9LO0lo7t4xk2gEsAJGe3MNTiOSDuf+26tet8e+CgzHEGiChIAAB0a+PGjdWqVJ05fcaVS5cFf1LY9hecorLkE8d/rlOrtjcMci3uaKlwsHPJhTb+ebJho+zsbFX3Rj/o18GGZmJf0969e504kxVyFNy4nrxm9Ue9evQsV6YsG6ZTslCfjWDLD+RvwKC/6P91lCsvijG9Nu7Ve/fuqf11AgQACQCA3tDQ5156xo5t2/Pz7OK2EJJDUKPJhBLr164jcRIb05O2+6ftIlgvC5+L2UJHqMQQ4CF1584dHFsZtLz/+PHjb86b36pFSzM3Ri0/hD8r2+YnslA94I68JbpRw/qHvz0kPsiSjwEiBxIAAAAJgozIYc+fOnmK2V9THzbvr2tOZS6iIm+cMmmyYM0AAuTMcDgLTzzJU0U+gZdx69atDZ+tHT54yOPVa/CnKxbBwqbxYjUe3IxmhQmSzWqePXt2Tk5OML55gHBDAgAA4OErikpJSenSqXPRUEA6dKBNI7xtJDyNgsgz9es9kXUvU8lnATil2uj7+qv4V/757Mys3bu+njxxUtPGTUzcsP0I/X0tLIfncwDB9F7kmQ7tE389dVryKxDPGAgQgZAAAAC4iCc/oi6cO9/gifre6N8kP9wnK/JncyTRAMI1O1UBgn5QhC/j56NJcSs+yTPq3r17B/btnzNrduJz7cvYYmnbfT5HFTzAwi/iQU7ZIL+uln4mc/Wq1Tau3yBTOQOgCUgAAACkkdv594e/q1yxEmsA7R7yPEp2wi/WIdjVJYAWIg54uT+/TicCBfDB74nhq+w/LS1t586ds2bMbNvmGZvF6mmE5o37BUP1I/RXkgbQqgDP9B3u4T5JNjVl0uSMjAxBGT/+qUGLkAAAABTBbuQb12+ItdrMXExAQ3x+5G/JBIBWAtBKg4S4+BvXkyXXD+CL/5jSUXDh3Pl1n60dO3pMsyZNSdDPNVg3Wcwx/LD9tKu66xmu+N/IE3jJh/5WLuH3jH0UE9WrR8/zZ8/J//PiXxs0BAkAAIBQQb7jrflv0lFQXEtUtK2wMFWuBsBmijFHlSY/acdfsix8a4Fw5YgSwAe5c6PAmZGWvn/vPnJmdn+hW+WKlSzu3iZ89RQ7S1nQHxNdmob+9Bl+7E6M4ykZ/du8eRRbmjdttm/PXtaET/wd4T8atAgJAACABxvwZ+zoMXSYfzbev3eUT3fzHt8dKIt0Fowx1a1dJycrWzJiQNAAfhS4BvM9dPDbpYuXDHi5f8P6DfjRaWi0Sov53VVS0TT5ZOceP4Ine6B6hB3hC02K2EGrU6v2hnXryQVB4cQp+KcGDUECAADgQm/eeXl5JNgiQRU/GjqL6Vl05TuAiCkcMdBk3rTxc7V3C7QkJSVl//79y5e9O3jgoCcbNqKteqxcabTg3GMtfGgjdVbYr6RcH2X/MgnAY9VrrFy+Ii8n16k4pEf0D9qCBAAADI2v1s/MzOza5Xl38SoL9wONITwTKpEHLZo3RUwQOQJqvS3z4uIV+hYZwMf7DrvdfvHixc2fb5o9c1aPbt1r1Xxc9QhYZ0uRapCYwiH8aU1dkQZ7dEa/GFOVSpWXLl6SmXEvoO8XQHOQAACA4Uje1FNSUtq0as3G+pSf8MvXQoMJ8l4SbezZsyf8uwYlEeggPJJPil9DB40hz99MvrFn9zfLl707YtjwVi1aJsTFo31OSBdWbUL+H/naOdZZnzzpqjNxv6Zq5SpLFi12zdfhQNAP+ocEAACMSDCk+p07d5o2bkJ7VbpDh2ImAKwPQOJz7cWfBRGohH06BTN28T+vX7++f+++91asHDt6TLu2z1YsX4HFoxbvbHFWzMkVyoVvIsXXznmK/729/GvVfHzl8hVZWVlKTg8AfUACAADGIr6jk+i/ydONPd0r3cOAcH15Aws46AwAZFUk8pP8LFCFkvY/MiNv+n3SbrefPXt2+/btixcuGj50WOuWrSoklDcXbbsvWcyPsThDuvAJgGA2Xxr9P9XoyTVr1uTl5Sk/WwD0AQkAABgRu8fT6J/2tow1W7iy/+IkAOS9FnPMM63bCD4FVOd3IBfJNED4rgLXkpx0/dDBbz/6cPXUyVN69+zVsH4DOl+Ep3TfOwk0DUDdo+8XaX9CR5JlQ3MWLaVWP2jW08K6R/NdpemDLp067971tfiLxv8sGAQSAAAwrju3f3O1/PGO+cNCfy5cCzTmiI6Jit6xY4faewZC8qX40n/Nd9xMvkFi/bWffjZn1uwBL/dv0ax5Qlw8HYiTtt2nPy3euiPx+UBPKldm6H1QWALNRaV0DB/VI2adLWwGNPZNxZUtN/qVUSdPnpRp+oUcAIwACQAAGA69waffTWvZvAUbG4SF/kXTgEBjjuinn3yKdvrkPwsiirC1T4HTnpt3+eKlb77e/eGqDyZPnNSnV+/GTz1drkxZGuuz0fcFgTubapd1/hZ1M6Utylw/6QMWmPJj0ZQg4cQit5BjThJyWjPT4In677zzTmpqquA0kD9JAPQKCQAAGFFWVlaH9olsjH9v+WthN0FvTBZwAvDB+6sEn4VIIkLQL+LevXunT5/eunUrCQdfHTuua5fn69WpG2u18eX6vprsixvtFJYxFy3RFzTp4Rv6u1crnFwCCUCIlrKxZQa83P/g/gMF+Q7xyeDrJAHQPSQAAKBz4ju63W7v1aMni8O8s/wWZ+EDO/K4fPnymZmZquxmBFISS4Wi3QVdFfmWL1+8tH/vvo8/WvPGzFlDBgxs1+aZx6pWw6g7WlgKG00VjthjEjTPK/yVVtGwRlk0l2vWpOny5cvv3LkT6DkJYARIAABAzwrcBM8MHjiIFu6yNhjFiFFYiw4+B5g0aZJ4XEgd8zUEfvEG1A/0xfR5h8ORnJx8+PDhdevWzZ87b/jQYZ06dHz8sZpWs4UV5LN2OFbMgKuFRbKmRTAnN+1LTWtgPO/yTuI74/XpJ0/8onwSXwADQgIAADok0+Pz9anTLCQc9ITvrNF/gAGKd/h29tPibm186tQpX5+uV746UwpSr2IfE8/UuWRxFKT8dufHo8e2bNq8ZNHiMaNGv/B81wZP1I+12lgzfavv+J4lbKgBiPyFNaNivSzoAysbXsk7yy/76itXrDR29JgD+/bn5+f7OksN9Y8JIA8JAADok+TN/sNVH9B23t5QI9ocVboYba9pzBFrttBwhLY6aN2yVfh3U11+J8F1SlXC+FoPe1l6evovP5/YvnXbu+8smzRhYs/uPZo83Ti+XBybQovFf/QZbjLdIt24+bb1fB9cK9KAyF5o9M8GR+KK/91/9fbWID9J3D9q5Ct7dn+Tn2f3W+SPBACAQQIAAIZA7v179uzh5/otyXCfrLyZhpU0+nx/5XtOd4sUweeqtMdhIpMD+J1kNysr6+yvZ3bv+nrVe++/PnXaS/1ebNWiJQnpxPNnsZjP+xVE86NqihdRGlBYM4AmQFpYXJm5YPIEV08A7zzKDZ6oP3XylO8Pf+ew58ucb/zzqAQA4CEBAABDOH3yVEJcPAsKPfGEO8goXhMg1grIkwyYLSm/3fG/HfqivNH/nTt3jhw5smHd+vlz5w0dPKR9u+eqV63GivN9HWfWwMMs7G8dJVnSTwuM+WJjNhoPiv+1tfDfET1JYq22zh07LXv7nV9PnaaF/YWnn7NI6b9kIzQlZymAcSABAADdYvf71DspdWrVdofshWN9Fg0iA4tOaDEkH52Q0ITEIMaMMArLVgucDnv+tStXD+zb/8maj2mhfvOmzSoklKetpASDbHKhv3BcF08TcPcEW1autTc/2y6LFNmvfFZQtPVI4Xcn7rqNJdIW+v8VExVNfj7ZsNH4V1/buX1HVlaW+KxzKivaxyRfAAJIAABAn9id3uFwdOnUmbYeEU/bVOwEgDUBYu1/DFjceP369T179qxYsWLi+Ak9u/doWL9BrNXGx/eWomXtbPYr8eJj1BfXuyxc2sCnEEWm6ML4+jpayIk0auQrmz/fdOvWLZnTTyas91UJAAAUEgAA0Dz5dufTp73Oyv6DF6MUTvlkdacBN5NvhHenJcjEOpKlpOInC0StKZzeon17bt6Z079u+3LrgjffGjZkaMvmLSrGJ6geKWIJaJGs97D4rJaRWYpk0ex/QTAhmiCRo4vgg+ink7yxXdtnp02Zun3rttu3bwf13wIAJCABAADNK/ASPEl+bv58k3fQz+KU9MsEQCzWIdF/i2bNI3bQcb6UVPnMXOnp6T8d+3HdZ2tnvD69b+8+Des3oMPqs7mWFEeKWDSw8M2rCp90z5zgo/N0NJtDw1OT5u56wc2sVzhOP02Srd4KHHrykNPp6SefGjl8xAfvryJnWl5Obmj/DQCgKCQAAKBtMi2AL124GF8ujg75Ly6JLNniabVCg5vXp04L+34Xk+SBcjgcFy5c+OKLL+bMmt2vT99aNR9nwyyy0E0w7DoSAD0tvsr++RJ9flBOfgQtz1y8dLR+72xrbLXsnKlYvkLic+0nTZi4ZvVHPx49lnUvU1Bxh7Y6AOGEBAAANEwQ/fMxRG5ubsvmLVgRJg1lghczcS3XY0z79uwNT/jCx0l+B9mUer9nycjI+O6771atWjVuzNg2rVrTIfZZq3rx/nK9bD1dM5EA6Gnx3SW6yJy7bOE7cgia+rCg/8mGjfq/9PLCtxbs3L4j6eo1p8N/N1zkAABhgwQAAPRp2pSpFq4BA51MNHgxk6fxA1ltrNUmGJ8k/PwOgZJ6J2XvN3vmz53Xp1fvenXq8h1qWQhIFlbiyyI/71hJnpJd+gA1ADpYyPcoGA6VngP8bGv83Mn8FLyCKRriypZr1qTpwP4D3pw3/4vNW06fPGW32wM9URH9A4QTEgAA0DbJHsB7dn8jGC8yuE2A3LmEp/izTavWYYtdlI91mJ2dfeTIkRXvLidhWcP6DQTjb1q4WcxizRZ+v4oU9Lp/ZW24PUcysN6iWCJ0EQ+0T3vL8KO1slea3dU+tGj/8cdqdmifOGrkK8vefufrr3ZdvXyFzsLLqqd8dcpXWHmFTAAgDJAAAIDepKam1qhelUaurhDW3f4nuGNB0rFEaQ3AuDFj1d1fGjDl5+f/eur0J2s+HjNqdMvmLWifXdZ6m/bRpEX7gtnQ6E9veb+nlNcqFeizMU9RA6Cnha/SKfKlmy316tR9vnOXsaPHkFh/x7bt5AQrSWUX5uIFiBxIAABAbwYPHsx30mWBbxBjJtoMmsTNZOWffvpp+PeRRlF5eXnHjh17++23e/XoWaVSZb5Jj9VTxBvND8wiaLfNGvxITphVZH+LNvlAHwCNLuK2/uQkKWOx1q1dp0P7xOFDh82d8wbJIfft2XvpwkV7bl4oxrZSWIsFACGFBAAANK8waHAU7Ny+I6idfaUXb1G6KxQ+efJk8HfE95O52TlHvvt+4VsLXni+a0JcvOoxZXCOp4/G5ZLZi/JcTuF0v+KRLvnsiE+WfG2P5OD3JVkEny74ONo3Q+Y4sDyNZWtWs6Vmjceead2mT6/eE14bv2TR4vXr1x86dCgpKSk/Pz9YJzAAaAUSAADQNj4+vpuSWqNa9TDM/OodCdEVWmVnZztLXHLp6+3s+RPHf168cFHHxA5lbLEsqrO4G2erHr4rX/jupIKAVXqRiobpyEuCVEHcoVnJ+tmLha/3EZf7nmlYOkDnN56fJ4ulEGwwTflEQjD6qqAhFl3IiUFD/O4vdBs18pVZM2a+t2Ll9q3bfjx67Mb15IJ8R+E0bwUoaAcwOiQAAKAHdLbaV0aMdEd14UgA3DGcqfbjtQo3QPmmit4iOb5n6p2UzZ9vGjZkKMlq+Kma+JBXMJBLhC++iuQln3e1TZcqhufbLAVUju4zvPaujT5gPbzlQ/yij+USHoUHwVb0KxY0xyfnQOOnnu7QPrH/Sy+PHT1m9sxZK5ev+Pzzzw8cOHD27NnU1FR62jgcDvlzD6E/ADiRAACAbhz74ailsNV7yBMA+imdOnR0KourJCcqlnzNzz///Oa8+W1atRYH/Xy5NXugrRwgwEU6/uaL1cWxviBPYDPUKiy8F5TcCwvvJXMPHzUS4qnT+OfLlSlLwvqnn3yqXdtne/XoOXTwkMkTJ82fO+/9le9tXL/h6692HT3yw+WLl+6mpPo6YYICKQGAASEBAADNIxGMw57follzc2HIGNrAlESBtNx3zKjRgcZPkpkA+UkSmFkzZtav94SZa4JCR++RbPvOj9GuoYUv/xa3vVGYAFhF5egyx4H9SfAWQdN//mWCgJ7PxCQHzIkvF0dC+UYNGpK0rUunzv369B05fMTE8RPmznnj7SVLP/pw9aaNn5OA/rtDh0/9cvLq5Supqal5eXky54bggfL0UnKETZm/AoAxIQEAAA3zBDQFzvdWrDS5ysLZoJahjWJZDcDsmbOcxYrPCryOHj06bdq0OrVq0y6bdGx+vr9p0WbiEoGsDhaZNvp+F74Rv8++BD6CePJkXNlyVSpVJsf/yYaNSA7ZMbFDj27dX37xpRHDhk94bfz0aa/Pnzvv3XeWrVn90brP1m7fum3/3n1Hvvv+l59PkDj+1o2b6XfT2By3kt94QCez8iY6gb4MoT8A8JAAAIC20WluK5av4Gq5EVWahMvmMI4CtHzZu/yWyG9n4QNHwZVLl+e9Mbde3dqskYnkKDcWb4sRG9eShJ+oVR9NgAQtZHx1eOWb05SPT6hWpWrd2nWaN23Wru2znTt26tOr99DBQ0a/MmrKpMlzZs1e+NYC8u2QwH392nU7tm3fs/ub7w4d/vmn4+fOnE26eo2cM5kZ93x9cVqcl0qQPMgX/GtlpwAgRJAAAIDmTZsylR+hJTwJAG0CtHH9hoA2NS8vb/PnmzomdvCOWlOk9TmtwXDtAtdYXFyAzTc0pzUGqkfwyhc+aYk1WyqXr1CvVm0SxCc+1753z15DBg1+bdyrs2bMXLJo8YerPti4ceP27dsPHDjwww8/nDx58sqVK3fu3Ll3714JTxglAXEEhsjF6Gge0LsAwDiQAACANvjqRHvl0uVYq80c3oYxtMCefOjO7TsUbvOtGzfnznmjWpWqnsLs0JTcyxwE/k/yw1zSJIRPSCRXYuH6s/IF9glx8fXrPdGmVetuXV8YPHDQpAkT35r/5gfvr9qyafOePXtIKH/27Nnk5GQSx8sPWQMAACGCBAAAtIcPHEmIafZMGuUObd2F5aFOAGgNAPmsbw8cdPorZL148eIrI0bSLIUFyragDuHPRgSyutfMd2+ljy1cXYH7cYy16Lg3XKzveUz7H3sGvSk6fA1JY1o2b9G3d5+xo8eQ4H71Bx/u2Lb96JEfSDKWk5UtM30smqQDAEQCJAAAoBn8iCj058kTv7C+s7RAnU8GQpoA0BqAw98eEmweQ7KUyxcvDRowULr7aVBrAEyiEYFYZ2I2lBA/RKbg7axZDl+WbzVb6tSq3TGxA8leFi1atGHDhsOHD1+6dCk3N1ewv0omMPb1MgAACD8kAACgYQP7D6Bl0qwgXPnoMSUKuL1NgA4d/JZuiSC6vXv37sTxE2KtNlN0DBsn3uqd7IlE27FmSxDbLFnc4b54VE02qr0gDeBDf5qWVK5YqX2758aOHvPeipW7d319/uy5vLw8yabkfictBgCACIcEAAC0QdwH4NyZs2Zv7MviYG/n2jAlAHwNAPPpp59WrVzFO1NvjORMscHtscCyC8Hz9BmaGPAdiG0Wa5tWrWm4f3D/gZs3b3o23SEcdd7XOPTBHa0SAADCCQkAAGhTgav43+ItUC+MdN3PhDoBYH0AWCdgGuneuf1bj27dzYVdEYoM6ykYsSeYNQDeJv6CUYOs3lb7VrOlRbPmY0aNXv3Bhz8d+5GV7stE9jJTSgX0LgAAiDRIAABAM/jo8+rlK6y8n+/eGurQnyUAru6zJvPnGzaykPfo0aOPVa9BA242kA5NAExcaB66AYv40ULJx8VabW3bPDN92uu7dn6VkZbOH0OFh9rvt1DydSJhAAAIPyQAAKBJkydOKjLPKxdSh2FiLNoEiATZH324mm7Pzp07y5Ur550grMj0va6ut6L5rYK+nayRT/16T0yaMHHvN3vYRFe8AmfhID3FDr4RygMAaBoSAADQHhLaVigfz49PH+aFJR6LFiwkAfV3hw7TUT6Dvn4TN6CnlUshrFzOY3O38El8rv3SxUvOnTmL1jgAACAPCQAAaM/K5StMMbSUXc0EgMTi48aMvZuSWq1K1eCOPkQbNUlWEdi4QUUtJjOJ+1d/8GHKb3dc/XcLCtvnIPoHAABfkAAAgJbQuLZRg4beaWvVSQBYYXzXLs+zxkhBXb9wiq4ivYdN5np16s6fO+/atWsyRwkAAEASEgAA0AYW1H536LDZ2wpfxQSAlsTXr/dE5YqVLEGd1pcmALQXgTmqNK3oYEX+Pbp1373ra34uZIT7AAAQECQAAKABfIw7cvgIVyhsjlE3AWCN8i0hGYOocAQhNqTPqJGvnD9/Xub4FGNkHgAAMCAkAACgGSSozbqXGV8uLkJqANi4+yFYf+HgoST0f3XsuJvJN8RHQ/CA/YroHwAAZCABAAAt2bBuvXfcTzUTALN3gi2zd/ph+jNo6zdF090cOnhI0lVXQ3+MvAkAAMGCBAAAtKRXj57eHrdqdgK2cEPx0EqA4FYFkFW1atHy2A9HXWP7AAAABBUSAADQjHvpGWVssRZzjLv4X53Qv3iLrzm/XCX9nuGMChv9x8eVfffdd/Pz80tyrOQrB9LT069du3by5MlDhw7t3Llzy5Yta9euff/991e8u3zl8hXk51vz31y6eAl5QJfPPvts06ZN5JUHDx4k77px40Z2dnaItg0AAEINCQAAaMb6tetcjW2i1ZwBoCQ5gGuyXm6wINev3gTA4q1GaNOq9eWLl8T7Lu7myz8vfk1ubu7Zs2d37fxqzeqP3pw3/9Wx43r37PVM6za1H69ls1jNRecaE1RlWMhvUdHiF5i9VR/kr+RBXNlyjRo07NKp8/Chw96YPWftp58dPfJDWupdZ4HPzeYfSO4LcgMAgDBAAgAAmtG3dx+Lqyhd5RkAirfQib3owk3iWzjEJ4mzZ06fQQv++bhZ3MdXcFjsdvuZ079u2bR54VsLXhkxsmNih1o1Hzd5OyhLLoKtsnJDGBWZcICbjEyyEoMlDKxHBFmqVKrcuWOnqZOnfL5h4/mz58StmGTSGF95DgAABBESAADQhry8vIS4eHdHW0+RueoxfUCLOSqahv50y73xtGtHSOhMdm3btm2+9p0VjdOfSUlJO7ZtX/DmWwP7D2jauAkt0RcU5Hs6KHuzDhbQe4+bp9ERa3fE0ipBoM+2k85LQBf6mDbEEnwR9CP4PhKVKlTs1aPnsrffOfbDUb/pDf880gAAgBBBAgAA2nDgwAHW/dfdCki6VX0kLzQB4EvcacufGtWq//LzCaeoPQ8Lgm/duLlz+47ZM2d16/pCtSpVaek+W6fNWyvConkW2ZtN0aaYKLLQB7TFkXcEVVccz4Xyrp98h2aWTtBmV3yewKYoJquymGMEK6eNmugExvRJ+hqyVKlUefDAQZ9v2Hg3JZXsF53OTLKZEKJ/AIDQQQIAANowbcpUfgBQrjBbM4u4mQ2JrRs8Ud810KejSBm/3W4/9cvJVe+9P2jAwDq1arOI3OpzurHCIJ6P0fngW7yw3KBw8dFkSOYt9BmWBljcXQRYLQH5yacHdGtdO2K2vPB817Vr12ZkZDh9hPvIAQAAQgQJAABoAIkFW7Vo6Wnc4u0EHJoZuMJUCUDjeBL9X7+W5Oo1W+B02PNPnvhl2dvv9OrRs0JCecHoot6GQzF8lC9ouiNIivjHggoTYdyvIAEQv8U9GbPng/h2Te5KiRhzVGnyMya6NM0N6E+rt1USayNUNrbMyy++dHD/AVdXAYx5CgAQFkgAAEAD7qVnWM0Wb8RPW5iYtZUAiEvuaz9WMykpKTk5+ZNPPhnYf0CVSpULw31PuCyM9QVROCvd5zIEhS2jon0sRXIV+dfzg7EK+grzzYHEi7g1EdnrJxs2Wrl8RfrdNLXPNQAA/UMCAAAasG/3N3x8yY9Oo7mFbfyLvfu0atacPcl6BQT9E/kG/d4Zi6MFrf9pk31xaO5JPLxjCvED/rCGSbFWG8lenqhbr1mTph3aJ/bq0bNPr979X3p52JChI4YNHz50GPtJnid/7dShY7u2z7Zo1rxRg4ZVK1chqZ3Fu/KK5Su8MXtO6p0Utc84AAA9QwIAAJFI0CX0jZmzWMsZwUia+lj4vQvF+l1Bv7sKhUbtNNxnQb+4bY+wt0B0TLkyZUl8T8L3Ca+Nf3vJ0i2bNn936PDZs2dTUlKUd9v1Nf7P3ZTUi+cvHD3yw5dbvli2bNm8efN+/vnnUJxXAADgRAIAABGLDxBf6NyFBrKaDv1ZKx1Bcx1W9s8P2RncD+U/hf+rz4kCzBYS7o8YNnz5snd37/r66tWrdMQeme9I+Z8UfukAABAiSAAAQAOqV64ibiSjuUyA1V2whkx891n+16AnAJKrFUz0W7VylYH9B6xcvuKH74/k5OTIjMxT8jAd0wADAKgICQAARDQSFCYlJYknrNXcIhmIsydD2quBzszFgn5+mP+EuPh+ffq+v/K9X0+dVhh/iycrCPQLVfgn5AMAACGCBAAAIhEfWe7YsUNmgEutLCzEF7Ri4kcFDcVi4yblZf13q1WpOmrkK7t2fpWdmSU+8r6+EYXfl8xrnP7qEATZhfwKAQCgeJAAAEDkoiHg4sWLrYVT3hZG/xoaCIhsc6x7rBtxDYCgXVAoPpoNuh9XttzggYMO7NvPT8Hr9BFq83+Vf2UJv99QrBkAAGQgAQCAiCOIBUeMGMGXZ1s1WwMg89fQRf8298Cdz7Rus2b1Rxlp6c4Cny1txOG4ZIDuqwhf+ShA4icl1uZ0OpxICQAAQgIJAABEtgJnu7bPqh6+R9QiHrTHFBPFRu7nB/e0mMz9+vT9/vvv1f4WAQAggiABAIDIVuCsVqWq6jF3hCy0EZFwyE73zLveCZJjzFGlSdxvs1jHjBp94cIFNK0BAAABJAAAENFys3NMGmzwE+qFhf5WbwLAZuyymi0jhg1PunoNg+oAAIAkJAAAENGuXLpsDlkYrYOFdiD2JAAxpg7tE+mAngj3AQDAFyQAABDRDh38FgkAW9ihYIOHehoCmaKrVam6+fNNTqmSfn4SX+QGAACABAAAItoXm7cgAWALaw1F436Le4Qf8mD40GHpd9P44yY/sicAABgZEgAAiFwkYP1w1QdIANhi80b8bGKvyhUrbf3iS5ljqGTIfwAAMBQkAAAQ0RYvXIQEgC10Vi8S99M04JnWba5fS1L7KwIAAI1BAgAAkYgVVE+f9rrqYbevpchI/LRg3jsaD10s5KmYKDZGJxuenz4jMzUY/RNt5GPmHtCVuNYZYxo5cmReXp66XxMAAGgREgAAiDh8M5VXx45TPdCXXGze0JxG/xbPVL5FYn322LWYYzz5gDtPcL/AT2G/OMfw5A8xpreXLEVjHgAAKB4kAAAQ0UYMG656rO8rOqct8r2hv2dETvpTXFdgs1ibNWn6Yt9+fXr1NhemCtLrN3nXT1do9Q77Qz4v1mpbv3adE635AQCguJAAAEAEoUEtH9oOHzpM9XA/oMUcFU1DdhLlV65YqW/vPu+vfO/48eP23Dyno8BZ4Hz5xZdYYx6fK+EH/PGukOQAJPr/asdODOUJAAAlgQQAACIOnwb069NXpq28alE+F5qzcJ8tTzZsNHP6jJ+O/Zifny/YtdMnT9HeAvI1APxEv66C/yhXsx8S/e/cuZNfGz/APwAAgEJIAAAgErES7j69eqse7ksG6J42OVzcXz4+Ydy4cceOHRPsBT8E55hRo/nuvEoSAPbrus/WOjGgJwAAlBgSAACIXCTGHTp4iOrhvnihU/CyIfkb1m+w6r33MzPuCTZesDvp6elxZcvRIX2U1wDQT1n41gLBOpEJAABA8SABAIAIwheZ058R2weAtfbZ/Pmm/Dy7U0EQvmHdejZkkHeYIN81DO4exvTxwP4D+HAfET8AAJQEEgAAiGgkAVBxIjC+IT7rikBD/8eq11iz+iO73a50TwqcL/bt516De4oA2Y4NdKIAm8k14VeTJk0yMzNDeYwBAMBYkAAAQISi5dwTx09QMQEQhOl03E+bxTp54qR76Rms1N9vkbyr0D7fUalCRZoAmGKibN7BQyUX8gLaQ6BsbJkzZ84o/BQAAAAlkAAAQESbNWOmqgmAp6EOm4qraeMmJ47/LG7w4zc6//mn4ybPoP6uGYLF2YWgBoD2MH5vxcoQHVgAADAsJAAAENEWvrVAxQTAO5Wvp8vv61On5eTksG1TXiRPXvnxR2vcTf9jXFP5upsAyVQCkM8iy3PPtnNNHRDgZwEAAMhDAgAAEYcPdld/8KG6CYAlOooE7hXLV9i5fYf8BsvH6BNeG88nAGb5GgD3qP+/njrt67AAAAAUGxIAAIg4fKS79Ysv1e0ETJanGj158fwF8bbx/M7J1aNbdxrxm2KiSAJgku8EHGOaMmmyzMcBAAAUGxIAAIhELPD9/vB36iYAzz3bLjU1NaBtlvT0k09Z6bS+3hoAmc+tWL7C3ZRUv+sEAAAoBiQAABDRLp6/oGIC0Ltnr5ysbLolglg80NC8auUqfA2A/E4tXbzEWYDoHwAAQgIJAABEtNzcXPnWMsEq6fcO+ONq9E+7/Pbp1TsvLy9YUbjNYnXXAHj7AHhG+nf1CqCJAf1cslRIKJ+RkUHfhRwAAACCDgkAAEQuOuttjWrVw5IAxJDonKYB5Nce3brbcwuj/xIG4iSN8fb69UT/ppgoOr4Q3QD66XTwnzdmzwnKhwIAAEhCAgAAke6Z1m3C0NqHFsbT0fdbNGuemXEviLuQnp5udn8ESwBoDQCL/mnlAx3852byDSeifwAACBkkAAAQ6QYNGBjq6J+VxJuiYx5/rGZy0nX26UEJxNPS0tgsYLT43z0VQBRt/+N53l0R8WLffk5HAf+hyAQAACC4kAAAQGQRx7tvzX/T6ik+D23xv8VkjrXafvzxx1DslMWz/YUJAA36+eJ/suz9Zo/k0UAaAAAAwYIEAAAijiDY3fz5Jpk5s4JVA0Db3y97+x3xBgSFzWLl+wB4EgBPQyDXQj69etVqdrs96B8NAADAQwIAABGNxOK/njpNS+hDWg9AO/76nc+r2B6rXoM29fEMA+oO/WlXYFYD8Nq4VyWPQIg2CQAAjAkJAABELhr75ufZy1ptLAcI0VKxfIVbN26Gbl8aP/W0lY71WbQGwBxVmo49ajGZfbX/AQAACCIkAACgAS2btzB5xst3D9VvjvEWnJck6C9sjWOOMX246gNnKGPuHt26eyc08Hwi2R1+LKD4cnHZ2dkh+nQAAAAGCQAAaMCoka+wTrS05Yx3zP6A2/rzCQCJyGnX23ZtnxWMveMMdjIwacJElgCQ7aePWQJAtqFn9x6h+FwAAAABJAAAoAEffbja7I3aaQm6d+LegEv9vdP9eob8p31/f/7peEi3n8T0K5evYLtgMcfQx7QPAN2SpYuXOBH9AwBA6CEBAAAN+PXUaTqRljmqtM0UI5hIK5AagCKdbunyUr8X6aeEtAbg6692sQSANQEimQCtxyC/HjlyJIgfBwAA4AsSAADQgPz8/IS4eE/xeXRUsRMAkjzQNbiyCHfYbbNYz/56Jgy7cPXyFb4JEG3RRPfC5p5/IDc314kaAAAACD0kAACgDd26vsDa8Lia0HhaAQXcBIhG/6wDwNDBQ5xhCbkL8h2VK1Zyj2XkSmBYHwD6uGnjJgj9AQAgPJAAAIA2LFm02D0QUBRNAGg/4OL1AaAroTUAx3/8SfxZIYnFC5xduzwv6APgSmPMrk4Iw4YMDe2nAwAAeCEBAABtOPbDUbO3DU+xmwCxibdcxfAxpueebUeL/8Uxd/Cj8ALnnFmz3Z2PXdkLSwBof4BFCxYG+eMAAAB8QAIAANrgsOdXLF/B6h63x8wNoBnQwjccIivZvHlzOHdhx44dZk9XBFdDIDqbAU0AtmzZEs4tAQAAI0MCAACa8VK/F9nQPcXrA+CtAXCtoVKFivn5+eHc/tTUVJvFavVMR8BNBhxj+vHHH+lr0P4HAABCDQkAAGjGpx9/whKA4vUBYGmDxWQeO3pM+KPtNq1aSyYAv/32W5i3BAAADAsJAABoxq0bN2n07x5Dszg1ALTVDa0BOHTokDPsJe5vzJ7jbQUUQ6sjaAJgt9vDuRkAAGBkSAAAQEs6tE90Ff+Xjoo1m4rXB4C+q2aNxxwOhzPsCcChg9+a3JMQ07GA6JCg1apUpX9F+x8AAAgDJAAAoBkkPl62bBktQaehc3GaALmH3RwzanT4N578tNvtlStWEjQBerJhozBvDAAAGBkSAADQkuvXr7P5dIuXANAhOL/YvIUVt4et3J1+0LAhQ+n2syZATRs3Cc8GAAAAOJEAAIDmtG3zjCU6ptg1AK53mS13U1KdKjW52bl9Bx2PiPZIJj87JnZA4x8AAAgbJAAAoDFLly6lXYHdLekD7ATsnkPgmdZtVNlyGuXn5OQkxMWbPZvk6gHcuWMn/gUAAAAhhQQAADSDxsdXr161ml0t+S3RAQ8DSsf/mTJpsmCdYd6LoYOHsATAYjL37N4DoT8AAIQNEgAA0ABBfNy5Yydz4KE/SwA+37BR3b3Yt2cvGwuIbE/HxA5OFP8DAEC4IAEAAM1gIfLG9RssJnPxagBI5H3+/HnxOsO5FwX5jseq16CzGZDtoQmAWtsDAABGgwQAALSBH7QnLye3QkL54lUClI9PyM/Pd6odas+aMdNMxwKKjunUoWP4hyQCAADDQgIAAFrC4uPxr75WjASAvKV9u+fEawv/Lly6cNHs7QTcsnmL8G8GAAAYFhIAANAAcaR+4vjPpsCbAFlM5mFDhqqyC1ThjhQ4X3i+K+0E3LB+A+nXAAAAhAASAADQBhoW8wF0m1ati1ED8Oa8+c6iQbY6AXeBc9uXW2kToJo1HlNhAwAAwKiQAACAxrB4fcOGDWZ3ob67N3A07eBrco/0L5MArPtsreTawox8rt1ur1unFtn+srFlnCj0BwCAcEECAAAaIA7TXV2B8/Ief6ymyTUrsCsBoJN80UUmAfju0GGnuD5BDeTT331nGUkAyFZlpKXzz6u4VQAAoHtIAABAY/jYfeFbC2gA7a4BiDZ5cwCZBODalatq70Ghe+kZdDijC+fO+381AABAMCABAAANEBSKs1/vpqSWscUKWgG5x9f3mQCQmFtynaog2zBn1mxWLwEAABAGSAAAQGMcDgf/66tjx9GeAOao0jaTnxqAWKvN6VC/8Q8v5bc7CXHxG9dvYM9EzrYBAIAuIQEAAA2QabKflJRks1hNnhoA12I2RftKAKpUqky720ZUkD1z+oy33nqLPo6oDQMAAF1CAgAA2iDZCoj+HD50GO0GYImOcvUEiInylQA82bARrQGIKDdv3nzttdfU3goAADAKJAAAoHmXLly0mi1KagDatGodgQNukjRm3759KPsHAIDwQAIAABrG6gEGDx7MJgaW6QPQMbGD5BoAAACMAwkAAGgbjeDPnTtns1hplM8yAfHSqUNHtbcXAABAZUgAAEAPSBowZtRoc4zJ5pkWQFENAIr/AQDAgJAAAIBO3Ey+UTa2DB0S1FcC0KF9otqbCQAAoDIkAACgeXRmgIKCglkzZpplmwCJ+wAAAAAYDRIAANA2fjzQ9PT0yhUryTQBSnyuvRMtfwAAwNiQAACATtCwftV778vUALRo1lztzQQAAFAZEgAA0JX8PHvTxk18JQBP1K2n9gYCAACoDAkAAOjNwYMHzSY6HViRxRxjql61WgTOBAwAABBOSAAAQPPEbfpffqmfKSaK5ACW6Ch32b8nAYgrW85ZgD4AAABgaEgAAEDz+H7A1PVrSQlx8RbvnAA218CgrmSAPFOQ71BvSwEAANSHBAAA9ECYAxQ4Vy5fERMVbTVbTNExZq4S4M7t3wTvAgAAMBQkAACgbSyI56N58tjhcLRp1ZpG/67ZwVxtgVwJwMkTv6i0pQAAABEBCQAAaB6fA/CPSaxvNVvo1GA2UwzJAcjjb77erdqGAgAARAAkAACgB74a88x7Y653bmBPE6CPP1oT5m0DAACIKEgAAECfaEqQn2dv0aw53wdgwZtvCV4DAABgKEgAAEDnfvn5RJlYq3c2ANO4MWP5vyIHAAAAo0ECAAB65orvC5xLFi32zgtm6tm9h9obBQAAoCYkAACgfw57fqcOHd05gKlRg4ZOFPwDAICBIQEAAJ2jQwNdv369auUqrknBLFa73a72RgEAAKgGCQAA6JagmH/Pnj2uCQFM5nNnzqq1SQAAAKpDAgAAeibIAeiooF/t2IkmQAAAYFhIAABA5/hYPz8///nOXd5Z+naR+YORCwAAgJEgAQAAPROX9KfeSVm0YGGRvyIBAAAAI0ECAADGQoJ+QSdgNAcCAABDQQIAAIbjcDjU3gQAAADVIAEAAAAAADAQJAAAYGho/wMAAEaDBAAADA0JAAAAGA0SAAAAAAAAA0ECAAAAAABgIEgAAMC4aPsftAICAABDQQIAAAAAAGAgSAAAAAAAAAwECQAAAAAAgIEgAQAAAAAAMBAkAAAAAAAABoIEAAAAAADAQJAAAAAAAAAYCBIAAAAAAAADQQIAAAAAAGAgSAAAAAAAAAwECQAAAAAAgIEgAQAAAAAAMBAkAAAAAAAABoIEAAAAAADAQJAAAAAAAAAYCBIAAAAAAAADQQIAAAAAAGAgSAAAAAAAAAwECQAAAAAAgIEgAQAAAAAAMBAkAAAAAAAABoIEAAAAAADAQJAAAAAAAAAYCBIAAAAAAAADQQIAAAAAAGAgSAAAAAAAAAwECQAAAAAAgIEgAQAAAAAAMBAkAAAAAAAABoIEAAAAAADAQJAAAAAAAAAYCBIAAAAAAAADQQIAAAAAAGAgSAAAAAAAAAwECQAAAAAAgIEgAQAAAAAAMBAkAAAAAAAABoIEAAAAAADAQJAAAAAAAAAYCBIAAAAAAAADQQIAAAAAAGAgSAAAAAAAAAwECQAAAAAAgIEgAQAAAAAAMBAkAAAAAAAABoIEAAAAAADAQJAAAAAAAAAYCBIAAAAAAAADQQIQiQoKCvgH7FclbwmnYmynhhS4qb0VOiE4kvyvgrNI/DJ8C6pT+BXgmwoUjpgO6PUypb89AgEkABFKMkJS8uLgfrRyDocjiNugOlz7wknyaAf0LwBBJ5OYSWJXAHxZVEDXbRw0HdDxF6qz3QEKCUBEC3/ZP72Lo8CPUVK6o+PrflCITyr+qMpH/zieYeY3GZP5k5G/tRLusgGPmI7pqU4AdzcdQwIQuYr3z1byf1HlaxBc5vRxdfDVEEX+9frY91CQafwj/xa+CRYOr7rCXAmpXcU4Y9l5jmOoXfJ3DS1+s+IWsFrcC5CHBCDilORmUJJ/Ufy3y1CYA+BGrpBkDYCv44bjGcnw7QQRDqYWiS/7OruZoiBGr5AARKLI+TdTWPInLrI1FGPudTGwA2W321etWtW6dWuLxfKvf/2rTJkynTt33rp1q8xbIMyUF2HqLNwpnpKU+Br2yqkPCr96jX7FGt1sUAIJQCTau3dvfU6DBg2eeOKJ+j6QPzVwGz9+fFA+nfzD37x5s1GjRnTNvj5XvG1NmzbNysoKyjaobuXKlX6PPH/8yYN9+/apvdURjXUSPXXqVEJCQinO7373O/qzRYsWd+/eZW/BvUcV7LB/9tlnfq8AzIIFC9TdbNUlJyfzlwv5Q9ehQwf2RpznWse+wezs7IMHD7733nuTJ0/u27dvYmJi69atyc20Xr16Cv+PIgS9r3355ZfifQTdQAIQifLz8+Pj41lgxD+Qcd999505cyZYrYA6duyo5EMF2zZv3rxgHACVZWRk/POf/1Sy70z58uV1Ng5SiBw/fvyRRx6ROZeqVq2alpam9maC62owf/585f8CQ4YMMXiIcPHiReWHKzo6Wu3theAg9+sDBw6MHDmySpUq5C6s/ByIfCtXrlT76EIIIQGIUBs3bqT/gQqjcPpKvlSJKvYt+ezZs76uZYJN4n/9z3/+w1cCaDQgmDRpkuQOsoJq8QHZvHkzvwaN7nio3b59+9FHH/V7Jrdu3RoHMBKQfF7Bhcdj8ODBam+vygJKAGJiYtTeXpAg051X/Kfk5OSJEycquaZpFBIAfUMCELnq1KlTSnECwF529OjRoHw6udj169fP78f9zo1/hlUCaCKGE2/kb7/99uCDD4pjfZm0p27dun5XCwRJUJWczKVENx4cT1UgAQgIEgB98DsSLnl848aNAQMG3H///ZJfrvJiuwiHBEDfkABEKHKJOXDgAP0nlL+aCMqnmzdvLh6ORmH8JHjZlStXHnjgAbZyFhP72h76vKASQPmnq45u55AhQ+SvieLdP3TokHg9ILBjxw7l2ewjjzxy584d+kYcT7UgAQgIEgDdkLnmOBwO8n/x4IMP8terUr7rhzUNCYC+IQGIROzq06ZNG+X/q+zSI9kbtRg5AHk8dOhQmQ8qJXXVI4/feOONoByHMBAclsuXL//pT3+S2V/xM23btsUAoDLoMcnNzS1XrpzkwfSlX79+am+70SEBCAgSAH2QGY84KSmJ1sxTOgv3xZAA6BsSgIh28uRJv52KxCE4a5EiqAEoRg5A28OUkmoAIxn601//9a9/paenB/tgBJ94+MIePXoIKjpkLvHkT+TbOX36NL825ACSXn/9df50VeL3v//9999/r/aGGxoSgIAgAdAT8Vwl5HL03//+V+YGQR+TC5fy0yDCIQHQNyQAEYrFkb169SqlIGwSNAT64osvBOsp9mZMmDBB5hN/5yXYSBLwlfQQhBE9SqdOnRKkW4L9Elf19unTRzwINHIAgUuXLtG2ZAFF/+RntWrVMLaSipAABAQJgM7wV/KDBw/+5S9/KSUV8Ysf6KZmAAmAviEBiDj8RYdEP9evX2cN8eXxF53KlSuzyKmEkwrfvXuXjdso0xFW8Dx5C60EiPCYmJ/C7Nlnn5XfL8Ffyf0gOTlZZrVAtW3blh26QG+NS5YsUXvzjQsJQECQAOjVTz/99Ne//pVdxCQvZboJ+nlIAPQNCUAkElQ+jhkzphj/umvWrCnGDN6SkxrOnj27lI8LnMxFcPr06cE8KCFDd/Pw4cMyB1NyQoZx48ZJrgp4W7dulTxVFPrHP/5x69YttXfCoJAABAQJgC7dvXvXYrEEfunSAyQA+oYEIOKIB/BJTU195JFHAo2fbDab3W4PyiZlZWX9+9//LhVgDEe2OSMjIygbEAaNGjUK6PD+85//5OesBR47h3Nycsh5SI+YzJiqkthrevfuLV45hAESgIAgAdAlJYMXy9cGaLdygCUAuOrqEhKACCVIA+bMmSP5/yl/ZXnnnXeCtTFLlixR+KH8C7TSE2Dnzp2BXqP1Medx0Ama7E+dOpU/JQI9yOxdgoFWITyQAAQECYBusDvvjh07lF+m/L5Gc/gaAOQA+oMEIBKJKwFycnKio6MDiqLIy0qXLp2ZmVnyjSHy8vKsVqtkSxhfn17KXUweyZUA7DhXq1ZNyZg/7KfJZCLfiNqbH+kuX74s2fdX4Z2SP+CVK1fOz89Xe4cMBwlAQJAA6AB/2yXo4MVKyIwYIXiNhtAEAKG/XiEBiDi+es2+9957pQKfc2TOnDnB2qr3338/0MsH2cLXX389wi8f69atY1vrd3fozw8++CDCd0oVgmNC+/4Ga3yMhQsXOn3/d0AoIAEICBIAndmwYYPCb1N50ZhW0B0hUQc9FBiNTZeQAGgG+Q+sUKGC4P/Tb4n1I488kpaWFpSAiawkLi6OX7mSKwjZgHv37pX800MkLy+vbNmyyq/a5JUVK1bE1VAGPdm2bt3q66YY0NFmL/7b3/528+ZNwadASCEBCAgSAJ2pV6+e8i+0FDcDgPxQoRoi6ASMG5/OIAGIUOL4hjzzxRdf8P+cCi8oEyZM8LXOQLeHlYgEFMNFck+A5cuXB3o8t2/f7kQAKis3N5f1/RUfXuV9SAR69Oih9p4ZCxKAgCAB0A1yeb98+bLfK5KM0qVLd+/eff78+atWrfryyy93u33zzTe7NYWNc437nS4hAdAAvlVigwYNAr0YPfTQQ7dv3xasqhifTh/XrFkzoE8nW/vwww+npqYG5VAEV1ZWVlRUFNvOUgpqVBo2bKj2VmvAlClTfJX6K28IJJkwHDhwgH0K7kmhhgQgIEgA9IFeWOjAG0qmLhFcqapVq7Zt2zZBYbkOLlY62AUQQAIQoXz9s8kPV+/r2jR06NBgbdhXX32l/HPZlZHVQkSUN954o5Ti0h36su+//17trY50ly9f/vOf/yw4buIjWQy0/RV6A4cNEoCAIAHQk8aNGysvp2CvHD9+vM4uUIj7dQwJgJbQf8XnnnuuVIDtKB544AESlgWrJ0DDhg19fZDkZZH8/Otf/6puJYB439PS0tgMx5I7Ii6BTkxMlFkhUM8884z82VhCb775phPHPyyQAAQECYA+kGuLw+F4+OGHA700zZ49G6MUgIYgAdAMdmU5c+bMfffdR684yuOqXr16CVYV0OfyD7799ltfnyKOoVkOEJSuCCUh+Nzx48fz2+y3oPr+++8/d+4cruxOH9NFU5s3bxYfSdY3LihIMsl6A+PrCCkkAAFBAqAD9JJy9uzZUgF2dWvZsqX4XgkQyZAAaNJLL73k9/Ik+CvJGUjm4PQOb+wsOqKi3wuW4AVt2rTx26pb8IKHH35YralzxXt3+/btBx98UHygJNud08f9+/fH9V0SOxrZ2dlms1myIsXXSfL000//7W9/C2igDPKa559/np3GEDpIAAKCBEA3tm7dyl9wlFyUSM6g9lYDBAYJgJawoCcpKYnFr34vTOxB+/bt2XqciqNYyfGIjh8/rqQNEgv76APVKwGYQYMG8cfH776Qo33jxg0n9xUYlszJM2nSJIVnI/XnP//5/PnzixYt4k8SJWcUsX///rDvuuEgAQgIEgDdWLx4cUCtFps0acLea/AbBGgIEgCtIsG036uSuJD+6NGjwWqk2LlzZ8myc8EG8M+rOxwQ2+XLly/ff//9ksfH15GcOHEivyoMh8ywo3rhwgW+76+vM4E/yOPHjydvzM/Pr1ixosxZJHlWx8fH66yzXQRCAhAQJAA6QC9o06ZNC+iKRDsmAWgLEgCtSk9P/9e//qXkTsNfwpo1axboB/Fl3nzacPbsWZm23b461NKYT7y2cOrWrZuvTRXHqeTBv//9b3K0VdnUSCb4+lq1aiX51UseavLAZDJlZmbS9+7bt0/BiSw0d+5cNfbbQJAABAQJgD6QK9urr75K724K6wHIFYx/u4obD6AcEgANe/PNN5Xfb5g9e/YU7wolfteLL75YSlSOLo9WAqg4VMKJEyfkW6WLLViwgL0dF3enVCfgLVu2+O1KIbBu3Tp+nV27dvX7RQj83//93/Xr18O980aCBCAgSAB0Y8SIEeybUnKPuHLlCsb/Ac1BAqBheXl5FotF/sIkDsvq1KnDr6QkF6xr16498MAD/MqVXCtpJQBtRRP+y6XfQSoF6YHVas3NzeXXgEs8Qw9FdnY2Ow8Vjk779NNPC1Zy48aNhx56KNDRQjt37qzazhsAEoCAIAHQDTZGnEKROdMlgDwkAJohGXeuXr06oOsUtWXLFpl1BrRJw4YNKxXgcGl//etfb926VZLPLTaZAUx9be2nn36KiF8SOyx++/4K3H///adOnRKsxOmdl038Fciv7ZtvvnHKdk2GYkMCEBAkALoxd+5c5V8lgYGJQYuQAGgYbZ1fpUqVgC5VROXKlYPVjZWE8g899FCpACcmGz16tDNc10r+Uxo0aBDQgapWrRr6+8ogx/bChQusFkjhtz9ixAjJwZTy8vISEhJKBVKbRMTFxZE3ijcsXMdAz5AABAQJgG58+OGHyr9K4qeffmLvxcUHtAIJgJaI216Tnzt37gzoUkV9/PHHbA0luWCR906YMCGgGoBS7lE1b9++XfIDEpBt27bx2+B3I4ldu3aFeSM1gT9hWrZsGdCJ97///Y/1qBafeLt371byBQnMnDnTKfXfASWEBCAgSAB0gF46Dh06FNAlaM2aNU7RHDsAEQ4JgPaIx+R56qmn/I7IKfirzWaz2+1B2Z60tLR//OMfSq6S/IaNGjUqKJ+uXJUqVZSPMlmKG9oZV3NftmzZovx7pz8/+OAD+l5f8XpiYqL8mSP20EMPXbt2DdO0BR0SgIAgAdCNlJQUJVcepm/fvvSNuPiAhiAB0IMjR44ov/Gwi9rbb79d8qsVXYNk6235T3/wwQfD1hOAbORnn30W6CHia3WBRwu6cnJyaN9f5QX2devW9btyEsoLJmlWsv4OHTo4MT9DsCEBCAgSAB1gpfgJCQnKr2x///vf7927x9ag6h4AKIUEQEtkriwdO3akVyLlhdyPPvpodnZ2UDYsKyvrf//7n5ILJT9A5CuvvBKUT/crPz8/Nja2VCABZZcuXXAdl/faa6+Jv1aZ7/33v//90aNH+TUIjjD7dcaMGQF1A6CvpO218K0FERKAgCAB0Ad6DaGDXCu8CpHXTJ48We0NBwgMEgCNkWznQPti0tltlSPXrNmzZwerEmDp0qUBfXqpMPYEeOedd3xNTCbpj3/8IzmeYdgwjSLf+Pnz58lR8nskeS+99JLTR8sfwUmYm5tbpkwZNs2cwo8oW7asYMBWKCEkAAFBAqAD7Fq0fft25RefUu7BzQ4cOCBYCUAkQwKgJfKXlYEDBwbae/KRRx4J1hy3drvdZrP5/UTBFoahJ0BWVtajjz4qsw1iQ4YMQV8uec2bN1dyJPkzLSUlRfJ48oeavWDHjh0K11yKq9iZPn16OA+C7iEBCAgSAN0gF6L8/Pz/9//+XynFVznysocffnjv3r1qbzuAUkgA9ICGTbdu3XrwwQcDzQEmTJjga4XKP5ryNXSaeJNYxPaXv/yFVgKELuCePXu2/FVb8Mxf//rX8I9QFJno18Ea1rNv5/PPPxd/lfLHdunSpYF++rPPPvs7N7nTtyhyOl2+fFlyL5DRFQMSgIAgAdCZMWPGiL84dsWTrFX+wx/+MHbs2LS0NKeWrzba3XIICBIAnaBdlyZPnqz8DkQvWCwE51fFVij/iYLH+fn5FSpUKMVFfjLRG/tTcCsBBFuVmpr6j3/8Q3J7fD2eMmUKBpRkxN1qs7KyTCYT/1Wytjq+vvHiTadw6dIlwTzTSjz33HNOH9+awb/KYkACEBAkADqTnJxMLkHi6xt7hv8T7//+7/969er1ySefnDp1KiUlha1QrUsQfzdHgQgwSAD0gP0bZ2Rk/Oc//1F+E6JXtCFDhgRrSzZs2CD5EeLH7MGDDz54/fp1Z4kvRpJvHzdunHgbfB0H8pMcvczMTPGqDHih9DWmNd/3lz964m+ZPXnw4MFifDr5OXXqVCUnsOD73b59u68VQkCQAAQECYD+DB06VHC18dWdzFd1ZaDVmEFE8pC//e1vjz76aFxcXKNGjTp27Eiu3mvWrPnll18cDoevSyIulcaBBEAnWKy2aNEihVcHdmF64IEHxA0nAvpQJ3fVePzxx5VfoegG8KFDsUsmBFUW5HFycjIbUNLXRwu8/fbb/GaAwNmzZ//0pz+V8pfU8bp160bfW4xDmpOTY7VaFZ5F7GeZMmXIGyVXiK81IEgAAoIEQH/S0tL++9//Sl5w5J8RPB/mHED+437/+9+TxKBdu3ZLliy5c+cOroqGhQRAb/Lz85V0xhVcJnr16uUMUni0a9euUgFe+EgGQisBSo7fhQEDBvjdcX7zSOBIjp5TdBxwfWRatGih/JDSXnEkDSve8aQv27JlS0DTt1FTp04Nyf4bDBKAgCAB0A3+GrV27dpSoppGJa1J5Z8MHZltE//pj3/8Y9u2bffv34+GrwaEBEAPBP+uSia9ElRc3nfffadPnxavUOZCQFt1S3YVePLJJ0v5u+oJrkQkegjWRYeuh9yM//CHP0hW1/rasPXr14svgmgryfZ948aNgmPod3DVuXPnClYi+av4E/kXtGzZUvkdlJ7Pf/7zn4tXqQU8JAABQQKgJ/xVaNCgQaVELRuVjw6k/KwoOYUZiLj3QoMGDY4cOaLuMYcwQwKgQ+SyVaNGjUCvFImJiU4fw7TLf5b4yYMHDwZ41Sr1pz/9KTk5ueQ7zh4///zzSj6XXcpr1qzpd7+Mhj8I4r6/km1b+cQyPj7ebrfLrNPvh1IkrmK9geW/Sn572rZtK14hvtaAIAEICBIA3RDPTNK4cWPBJU7wq98RL1TpCSA/QoPgmkleTP6LyaVercMOYYYEQIfIxevrr7+WuSgIrlnsVzpRa1BygDZt2gR0nSLbMGjQoKDsO/l5/PjxQLte7d27V2ZnDRs4sgqQ8ePHlwrwHvbVV1+JV6X8Q/nHkj2P+ZNH8vmtW7cG9LkggAQgIEgA9EHyipGZmVm7du1Ag3i1uv/K1NP6fVfVqlUvXboU/sMO4YcEQLeaN28eaA+kpk2bBhot+RotlOQS4iuRvJL3BGBb0qpVq4AajpNjVZLP1b1z587ReX+VoEe1ffv2wfp08rVmZ2ebzWaFG8BYrVbyxmBthgEhAQgIEgB9IznAM888I/ge1QrxlfAVAMh3WSY///nPf/70009sx1GGoldIAHSL/AMXo+ZRviBcHv/GuXPnyl9rJK9HJY8hyDYcOHBA+f5SJ06ccOIy51uTJk0COov+8pe/XLlyJYgbQL4aOsKs3/52gr9OmjQpiJthNEgAAoIEQPfIhWjixIn33Xef/EBAag3+w/PbV0GyWzOfA5w/f17t4w2hhQRAz7p27RroVaNu3boBxcGSfWRTUlL+/ve/l+IaICq8DpawEoBuxhNPPKFwZ+klsnv37gj9fSFHZv369fwRkzmY7PGUKVNK/rmCB053HhLQ6UTPqAsXLohXC0ogAQgIEgDdoxeQw4cPJyQklBIFzaX8TRSg/PQIHb+NY9mOlC9fPjMz04nLpn4hAdAt8k8rnktVyTVoy5Yt/ErkP4J/wH4dPny44Gqi5KpEH5SwJ8CXX35Zypt4KLnMCeZAwJWOoYeC3ACio6MD+h4tFktubm6wNoB35syZP/7xj4HWa7Vq1crXCkEeEoCAIAHQPXYNyc/PX758eenSpdl1r5RUrC8oAgtDDiBf3i/5q3iz2db27dvXiSunfiEB0LmhQ4cG2h22cuXKdIhPhQRXh/Pnz99///3yFyOZaxOJyK9evVq8nSWbTTZe+Z6SzyW5Cq5uMsaNGxfQyUNevGnTpmB9uvirUT61M/+azZs3B2uTDAUJQECQAOibeGDovLy8NWvW1KtXr1SElfSLR/n0S3L7yePDhw87fVT1g9YhAdAz8u/622//v727D7aiLPwAzq/L5bVEhQR5E3NswsgXZioKYtSQhMl+ouIYzURlM2BNDZYIMspYk5GiDmZmb2qTJIopJuXU1GBjFpUWJhiaQYAN/IHIFUQFce5vf21u69lzzt2999x77jnP5/PHncPhnN1nn93z7PPdl2efHzJkSPaHXb0VuPvuu/NMvOSf8TuzZ8/OP7uyn5w/f37nlvfHP/5x/vlGHzviiCNeeOGFsotD+xtH3PsUOXw1Y8aM+Lu1fapD8uKll15Kjrrl3KiiF8cff7yx7TpBAChEAAhKuk/83HPP3XzzzVHrN3To0GwT1JOyh/bLtt4d3hacvDNlypT08tJMBIAmF/1or7nmmqIt0QknnJAdwT2PRx99tGw7kvPa8fifffv27cRTnKICpx+BnGeRr7vuupK66sQiN7H4mvv8vf/+/fs/++yzXZ9v9RWxevXq/Ks4cdVVV3W9YKERAAoRAGj/95hpa9euveWWWy6//PJLLrlk7ty5s2bN+t+e8rGPfezcc8+NXkR/Z86c+cEPfvCkk04aOHBgnwrH+NMqvb9+/fp6VyrdQgBofq+88sqxxx6b85xg0tv7zne+U3RGUb9t0qRJZaeWe7f4H/PmzSs691tvvbXQLEaNGpUcFdb1z7rvvvsqbR6VLF68uGfKNm3atGLb078vLYt2zD1TvKYhABQiAASu0lnxuhQj/ff111/fuHHjddddN2HChCrbZKVxgT7xiU9kl44mIAAE4fvf/36eH3z6/ZEjR2b7x9WbgHvuuSf//q96M9Ta2hqfBCh76WH2QVFRUUeMGFGyXNXddttteRYqBNkaiOozvvc3v9GjR+/fv7+9o6tFa1LbmzZtSu4zya/S0x5sAJUIAIUIAPRy8ahuSdue8/7AgQMHuoSyKQkATS65V2n8+PHpn3Q262cbguuvvz47wUr3B0ezyPOopupnIdP/O2/evPyP5v3GN75RslzVRzk46aSTogXR82sv11+PXi9ZsqTseqn0ZvT67rvv7rA+a3InWfz1yy67rNB9wLEa3qAcAgGgEAGAhrBnz54zzzyzelNZ8n56bECahgAQijVr1vzPGyr95ks6ykOHDm1ra0umEHf9kz5cSTdu+fLl+Xd+eQZM6NevX84Hku/du/fII4+sPrWS//rpT38af9fgBolknSb3/vbJ9PLjF+nLyeI3zzjjjOxpmRJf+tKXjuuCcePGjR07Nvob//PYY4/Nv70loi8eOHCgw6ISEwAKEQBoFFEz+L73vS9qyZMuQbpvkH3x+c9/vt5FpvYEgIBMnjw5vQfKc4H+1VdfnZ5Cpd7Snj17hgwZ0uH5xOzFhdU/lgwHVL2XtmjRoirLlZ1LMqxBh8sVguzlVWeffXaVlZL9Z0tLy5NPPll9shs2bCj6VLjqZejwUQ+VNoAlS5a0vzn41etS3d5PAChEAKCBbNmyJT3IW5XmNGpso7RQ7/JSewJA08peuP/b3/625IddtleXbg4GDRq0e/fuslNLd6GifX8nBv3s8CvJSYBKh2yj17t27UqGOMg5Atrvfve7bEWF3AVM10D15/6mDxclb8Y9vyrVGL0ZD5VdWx1uP2XLH21UzzzzTMmyU5YAUIgAQKOI271LLrkk5zG7AQMGHD58uN6lpsYEgCZXcnTznHPOSf+w8/TaFyxYUGXi7Z168ld+Ze8ESBYq+jt//vzsiYVsPzV5MWvWrHThs6+DFVXCgQMHRo0aVbIKyga25PUxxxzT1tZWqd8fv7jrrruyX+y0Ktew5fSRj3ykZ6u2UQkAhQgA9HIle73HH3882SA7bEu3bt1ax5LTHQSAsGzatCm+7C/78650yLx///7bt2+vdAYgct555+Xf7WXnWF2/fv127NhRqbP+j3/8o2/fvnmmH7+OPvy3v/0tXSElx60DTALpRb788sv7vLnHX+Wq0Njtt99effpRqEgPOpF/88izQot+K3H//fcHuK6LEgAKEQDozcoeSsvePlfpas9HHnmkLsWm+wgAwfnMZz5TpWNU9pcffaW9Quf40UcfLdSrmzp1atlZVPHJT34yO9/4JMCcOXMqdUzLTvmzn/1s2TpxFVBk8+bNlcbWrJQYJ02aVGlUqPY36nPp0qUdruLuli3/mDFjSu4GJksAKEQAoJfLtnjpPXL1wz133XVXXcpM9xEAgrNjx44BAwZU+pGX1dLSEvUOs5PKPvkrKz3xkSNHvvTSS6ecckqemSb69u0bP1+2pPF64oknCmWPaKl37typz1dWVC1nnXVWzvWYvPPYY49Vn2zUJerfv3/XL9rpDosWLeqZum1cAkAhAgANZ86cOSVbZqVDad/97nfrXVhqTAAI0cKFC4v2w2bPnt2e6YIXffLX9773vehbDz74YKFvRebOnRvPMV2AmTNn9nnzkJRVRMt7xRVXJN8VA0rce++9fTq63D8tqvaLL744O52Sij3//PN7rMdf9KKg1tbWp59+2pZQhQBQiABAb1b2Stpo35qzzbzxxhvrVHC6iwAQor179x511FHpzlAef/7zn9MTefXVV9NP/qoyoFD898QTT3zttdfiS3cmT55cqLsWdTfjkwDtb7RcjzzySP7yR59JP9OgylUrYdq/f//o0aNLhlGqUpmRI488Mh4eqop169aVfCvnGu+KnHOJPzZt2rSeqeEGJQAUIgDQQOI9aaUAkG2xly1b5nBJkxEAghN3wZcvX57zwozkv84+++z0dK6//vqyn6w0qXvuuSf5brprWF0yzeQkQCwdITo8CRB9Mn6qsfarRFwhixcv7lPu8H+Vi8S+9a1vVZ9yFPbe/e5316XTn3Ngu9jq1at7pKYbkgBQiABAw5k+fXrOZvOGG26od2GpMQEgUAcOHBgzZkyHP/sSv/nNb+Kv79mzJx49IN1AVHk9ceLEkhttyz5tqlLT0yd1J0Bk7dq12RlV+frYsWMPHjxYl3rutZIV8fTTT0d1mycBJq9PPfXUQ4cOZSeVfufmm2/OuX67qIs3GIwePfrFF1/suXpvKAJAIQIAvV9Jc508Ur3DxrPDgz40HAEgRHETcMcdd+TfXcWtw5QpU+LvLliwoNB3f/nLX5aUIT0CcfWZJg1TPBzQ4cOH3/Oe9xTq6t15550lyx6ykitBp02b1qdc61/lnSrjwcUTf/7554866qievN+30/OKvnjZZZfZKsoSAAoRAOi1yg51vXnz5vxb7I9+9KM6lZ3uIgCE6/XXX58wYUL+339s7dq1zzzzTMno+1WuwYhen3766WULcMEFF1SZUbZL19ra+uyzz65cubL6x0qcfPLJLvqPZbu5q1evLnol2Jw5czqc0bZt29b82wMPPLCmp9x///3R7DrcpLPL29LS8tRTT3VPlTc2AaAQAYBeruQ8/KJFi/pkDrRVkpz/p2kIAEH72c9+ln+PFTcQp5xyyrnnnlvpf8v6wx/+UHbumzdvjvpeHc4x7eMf//g73vGOQod7f/7znzu+215uCIj43t/q665kRbz1rW/917/+1QvrM9mxJXeH55EsV6WMGjgBoBABgN4vab137dp1xBFHVG8Y07Zs2VLfklNzAkDoPvShD1X58Xflyor4b5QWsjNN2qBPf/rTnZt+9fkmdOxKpPvuixYtyt/1j1177bV1LHxOlUa2rr6wq1atir8e3yVf30XoJQSAQjoXABprY2us0nZC+hh5oy9spfLHp8TPOeec/JvroEGDDh8+3LPFp9sJAEGLGog//vGP6d95Da/bjofvLLm4It27il5s3769f//+NSxASVfvT3/6Uz3qtZdK7w+i9VJyHVelyky8853v7M33Uieb1s6dO9/2treVXYoqW9fIkSP37dtX9krZYAkAhRQKAOPGjcuelOudW13vLFXPaNZlj36tHW6i6dZyypQprqRtPgJA0OI+03nnnZftJHX92H/091Of+lQyo/ZyjWn0TtQSFRrGscPWKnlxwQUX9HSF9m7p9HX66acXrdiHHnqovuXPb/ny5Z3Yoi699NL463Z1MQGgkE5fAtT7e5mBnxZr3GXPlvyFF1648MIL82+osYULF9al/HQrAYD/vxa/ZCj9mmSA1tbW7du3J3uOShlg9+7dgwcPTn+xJlpaWpJhQ2l/8ygQq1atKlrbs2bNau/1O8KkeIcOHRo/fnzJInQ40HW0zWzcuLG+i9CrCACFdCUANEQPuyEKWUO9+bRMTunCv/jiiytWrBg+fHjO9jD9z/gO4IauCrIEgECVXOowb968PI1CHskTBBcsWJCdXdkyXHnllV2caXru8d/Pfe5z3V+LjSeq83379o0cObLQ2hwwYMDWrVt7f+ufLuGvf/3rohtz9PmpU6e6CighABRSKACMGjUq+k1t27Yt/vvP3iouW1tbW2i/i5dffjlZ/Ggd1XUlFBAVeMuWLfF29eSTT65fv/6+++772te+NnPmzKgZTxq6Qg3jsGHD3ADQlAQA/r9B37lz56BBg2py+29s8ODBu3fvrj7T5PXevXuPPvroKs+d7cTcd+3a1f0115AWLlyYVFTOql66dGm9S11NpR5JJ850R1auXNnD5e+1BIBC/lkkAPTJPMK8J5+bUdSKFSsC6fe3v9GePPzwwzXcJfWkso9yL3kz5zM04xfJsbxwtoFACADByd55Fr9YsmRJDe8EuPrqqws1Ftdee23nZlQiLvBVV13VrrUqZ9OmTfFzf3PuACLjxo175ZVX6l3wikrWcnqT3rFjR9Gry6KPDR8+vK2trR6L0usIAIUUDQAlG16nv9sDoi0hXsamb1STy37WrVsXL3svXzUdKnuLXf6Fij7597//vd6rhW4hAISo7KWcUafn6KOPrkmLM2zYsP379+efdfu/z7eOGDGiVnPft29f7Wut8UU1f+aZZ8a1lHMA0OjvT37yk0bZ62fLuWzZsk5sQl/4whfqUv7eRgAopHMBoIbnXbtPEgDC8fDDD/fpxWukkjwPc+zwk+nPGEujiQkA/NdNN92Uv2moYsWKFZ2Y+7e//e2c069evG9+85uN0mHtbiVne+J7f8squRohMX369OykGsjBgwdPPPHEPJtNWktLy1//+tf0dJLjgg1aD50jABTSlTMAvVywASAcZZvH1tZWY2k0MQGA/3r11VfHjRvXiSYj3XaMGTMmmk48wZz3jcX/e+jQofxP+S076+j18ccfH02nZMphKln25N7f7PWglao3av03b95caYKN4qGHHupT5MR3/JnJkyeXTCe0myDbBYCCBIBmEloA6FOueYwvpqVZCQC8yZ133lm2IajUWGQ/eccdd6QnmLO3FH9s5cqVnWunEsnzXAOXrfYvf/nLeVZlOhtEX2mvcNNIY5k1a1ae7arED3/4w3oXvM4EgEIEgGYSTgBIn/5N71vf+973JsfyaEoCAG9y+PDh0047LWfDkT2Q/K53vavsQ5Rydhyj706YMKHQAAVpEydObNAeas2VXKzy1FNPtba2VllxWcOHD690I0fD2bZtW/zA6TxDXyefie8Grj6CbXMTAAoRAJpJOAEgkW4eR4wYsX379vbwrnsMigBAqV/84hfZ5iCnBx54oOThKYXOAER/H3zwwaKtVfLiV7/6VRMcrq6hpO0+44wz8ldj/CI9GmajPxAnKvlXv/rVnJtT+p/x0yQqDTTU9ASAQgSAZhJIACi7lx8yZMjjjz/e6M0+HRIA+K/kp55/rJj47GH8sfe///3x4f8uNhmTJk3K32YlR20//OEP16AKmkV6FWQvrKpyQXxyBXwTtP5x4eNt8uWXXz7hhBOKblfR5v2Xv/yl7GRDIAAUIgA0k0ACQJ/MjmDYsGHr16+PKyGcti5MAgBlROk/5zCR6X9GLWZ6IkUPxiefKdryxne1lnTUtFyxtra24cOHZ9dmlfXb0tKyYcOG7KQaukqjwq9duzb/FpW8juJoQy94VwgAhQgAzSSQAFByMGjixInxsD/BnvYMigDAmyS/84suuihn2xE766yzshPpxFHk+MPTp0/PM9PEnDlzalYFzSKuyUsvvbRSW19pWOj4upfmULLtzZgxI+cmna6c22+/Pcz9nwBQiADQTAIJAImWlpbFixcfPHiw3hVPzxEAKG/Lli0DBgzINhNle41R2/HEE0/UcO4bNmx4y1vekueWzcjAgQO3bt1aw7k3uqS3unHjxmjVFNoNDB069Pnnn69v+btPtFXHdwMXcswxx+zZs6feZe9R8SZ044035r8jPw4AYSalWEkAaLhnSFUSLUgUAEK7vao5AkClc78lB4M++tGPxiM+u+U3KAIAZcRNwA033NCn3IXR2X9eeeWVXZ9diSuuuKJKg5Uuw0033dSVuTerqFanTp2afw8RX0l166231rvgNVZyX0q0reZ/JkBi3rx5yQTD2UHGZwBy5vDAzwBEW8W2bdsqVU7jihehc892bGjr1q1L10DjqlL+/v37z507t+TgXTjtGwIA/5E9wBP1nC6++OLqbUr04qKLLnrttddqOPfY4cOHZ8+enfTVKt2x+sUvflGDVSL9UIVKVVcivpn7tNNOKzuKa4MqexnrgQMHxo4dm3MHmf7MY489Vqfl6FHpQ4BRAMjf+4kCQJjPS05s3bq10EbVKKJliQJAIGcAOn0rWi+RZ4jnt7/97eeff/6qVava2traM5fstjf1+iVNAKAD11xzTXoI+bTo/a985Stx779WTUa6DYoywNKlS/v169en3BmAwYMH33LLLTWZaZOJqu7QoUOTJk067rjjxo0bd1xuv//979ubvfWPlm7NmjXRwkYxIH/9RB++8MIL0xOp4yJ0q/Si/eAHP0gWv3r9RNUYNQXtAff+I88991xcV1FtFNq6erN4WW677bZ6125PW79+fbL4jbUe4wJHxo8ff+qpp06dOnXGjBnz58//+te/fu+990YxNfsLTR/3Cfb3GyABgFLZgfy3b9++aNGiqCmJr5+O+v0nn3xy9E78oJBazbHSsed47tEc4xwyaNCgD3zgA8uWLdu9e3dN5t5kit54nf180+wAShakK4+o6+JXGksnNgZjhrQHUwnNulxZDb2kVbbGSsvV0MtLJwgA/EehjmNXxvnJOeUq7zfBEPXdqis100x1W6v9XPbzzVE/ZXViYZu4NjpBbTSHBm0Jk1NwHZa/ygU/DbfUdI4AQHllDxhUOYrQ9SajUntUJWy4ZrFDRU8FFP1WL1eyO0y/38UJNreiG0MIdZJHs54ECLOlbdylrn7sI88OnRAIAHRVFxuOQo1spQZL41WFyinRiWN7wdZhV04MhibnkVd6vxB2Ls26XOQnAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAAIiAAAAQEAEAAAACIgAAAAAAREAAAAgIAIAAAAERAAAAICACAAAABAQAQAAAALyf6bTKs5jpopFAAAAAElFTkSuQmCC");
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vanilla/frontend/main.js b/v2/internal/templates/templates/vanilla/frontend/main.js
new file mode 100644
index 000000000..d54d4ded7
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/frontend/main.js
@@ -0,0 +1,16 @@
+// Get input + focus
+var nameElement = document.getElementById("name");
+nameElement.focus();
+
+// Stup the greet function
+window.greet = function () {
+
+ // Get name
+ var name = nameElement.value;
+
+ // Call Basic.Greet(name)
+ window.backend.main.Basic.Greet(name).then((result) => {
+ // Update result with data back from Basic.Greet()
+ document.getElementById("result").innerText = result;
+ });
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vanilla/go.mod.tmpl b/v2/internal/templates/templates/vanilla/go.mod.tmpl
new file mode 100644
index 000000000..21bd6c6a2
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/go.mod.tmpl
@@ -0,0 +1,9 @@
+module test
+
+go 1.13
+
+require (
+ github.com/wailsapp/wails/v2 v2.0.0-alpha
+)
+
+replace github.com/wailsapp/wails/v2 v2.0.0-alpha => {{.WailsDirectory}}
diff --git a/v2/internal/templates/templates/vanilla/main.tmpl.go b/v2/internal/templates/templates/vanilla/main.tmpl.go
new file mode 100644
index 000000000..5656d03e3
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/main.tmpl.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+ wails "github.com/wailsapp/wails/v2"
+)
+
+func main() {
+
+ // Create application with options
+ app := wails.CreateApp("{{.ProjectName}}", 1024, 768)
+
+ app.Bind(newBasic())
+
+ app.Run()
+}
diff --git a/v2/internal/templates/templates/vanilla/template.json b/v2/internal/templates/templates/vanilla/template.json
new file mode 100644
index 000000000..734c87a64
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/template.json
@@ -0,0 +1,7 @@
+{
+ "name": "Vanilla HTML/JS/CSS",
+ "shortname": "vanilla",
+ "author": "Lea Anthony",
+ "description": "A simple template using only HTML/CSS/JS",
+ "helpurl": "https://wails.app/help/templates/vanilla"
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vanilla/wails.tmpl.json b/v2/internal/templates/templates/vanilla/wails.tmpl.json
new file mode 100644
index 000000000..0386d2ba3
--- /dev/null
+++ b/v2/internal/templates/templates/vanilla/wails.tmpl.json
@@ -0,0 +1,5 @@
+{
+ "name": "{{.ProjectName}}",
+ "outputfilename": "{{.BinaryName}}",
+ "html": "frontend/index.html"
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vuebasic/template.json b/v2/internal/templates/templates/vuebasic/template.json
new file mode 100644
index 000000000..228b5ceeb
--- /dev/null
+++ b/v2/internal/templates/templates/vuebasic/template.json
@@ -0,0 +1,6 @@
+{
+ "name": "Vue2/Webpack Basic",
+ "shortname": "vue",
+ "author": "Lea Anthony",
+ "description": "A basic template using Vue 2 and bundled using Webpack 4"
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vuetify-basic/template.json b/v2/internal/templates/templates/vuetify-basic/template.json
new file mode 100644
index 000000000..bf557f471
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify-basic/template.json
@@ -0,0 +1,6 @@
+{
+ "name": "Vuetify1.5/Webpack Basic",
+ "shortname": "vuetify1",
+ "author": "Lea Anthony ",
+ "description": "Basic template using Vuetify v1.5 and bundled using Webpack"
+}
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vuetify2-basic/basic.tmpl.go b/v2/internal/templates/templates/vuetify2-basic/basic.tmpl.go
new file mode 100644
index 000000000..b9b1d419e
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/basic.tmpl.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "fmt"
+
+ wails "github.com/wailsapp/wails/v2"
+)
+
+// Basic application struct
+type Basic struct {
+ runtime *wails.Runtime
+}
+
+// newBasic creates a new Basic application struct
+func newBasic() *Basic {
+ return &Basic{}
+}
+
+// WailsInit is called at application startup
+func (b *Basic) WailsInit(runtime *wails.Runtime) error {
+ // Perform your setup here
+ b.runtime = runtime
+ runtime.Window.SetTitle("{{.ProjectName}}")
+ return nil
+}
+
+// WailsShutdown is called at application termination
+func (b *Basic) WailsShutdown() {
+ // Perform your teardown here
+}
+
+// Greet returns a greeting for the given name
+func (b *Basic) Greet(name string) string {
+ return fmt.Sprintf("Hello %s!", name)
+}
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/babel.config.js b/v2/internal/templates/templates/vuetify2-basic/frontend/babel.config.js
new file mode 100644
index 000000000..57e6d0a51
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/frontend/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ presets: [
+ [ '@vue/app', { useBuiltIns: 'entry' } ]
+ ]
+};
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/package.json b/v2/internal/templates/templates/vuetify2-basic/frontend/package.json
new file mode 100644
index 000000000..9f0cac08e
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/frontend/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "vuetify2",
+ "author": "Travis McLane",
+ "private": true,
+ "scripts": {
+ "serve": "vue-cli-service serve",
+ "build": "vue-cli-service build",
+ "lint": "vue-cli-service lint"
+ },
+ "dependencies": {
+ "core-js": "^3.6.1",
+ "regenerator-runtime": "^0.13.3",
+ "vue": "^2.5.22",
+ "vuetify": "^2.2.17"
+ },
+ "devDependencies": {
+ "@mdi/font": "^4.3.95",
+ "@vue/cli-plugin-babel": "^3.4.0",
+ "@vue/cli-plugin-eslint": "^3.4.0",
+ "@vue/cli-service": "^3.4.0",
+ "babel-eslint": "^10.0.1",
+ "eslint": "^5.8.0",
+ "eslint-plugin-vue": "^5.0.0",
+ "eventsource-polyfill": "^0.9.6",
+ "vue-template-compiler": "^2.5.21",
+ "webpack-hot-middleware": "^2.24.3"
+ },
+ "eslintConfig": {
+ "root": true,
+ "env": {
+ "node": true
+ },
+ "extends": [
+ "plugin:vue/essential",
+ "eslint:recommended"
+ ],
+ "rules": {},
+ "parserOptions": {
+ "parser": "babel-eslint"
+ }
+ },
+ "postcss": {
+ "plugins": {
+ "autoprefixer": {}
+ }
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "not ie <= 8"
+ ]
+}
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/public/index.html b/v2/internal/templates/templates/vuetify2-basic/frontend/public/index.html
new file mode 100644
index 000000000..570f9f0de
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/frontend/public/index.html
@@ -0,0 +1 @@
+Wails
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/src/App.vue b/v2/internal/templates/templates/vuetify2-basic/frontend/src/App.vue
new file mode 100644
index 000000000..2d4a34228
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/frontend/src/App.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+ mdi-view-dashboard
+
+
+ Dashboard
+
+
+
+
+ mdi-settings
+
+
+ Settings
+
+
+
+
+
+
+ Application
+
+
+
+
+
+
+
+
+
+ © You
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/src/assets/images/logo.png b/v2/internal/templates/templates/vuetify2-basic/frontend/src/assets/images/logo.png
new file mode 100644
index 000000000..31fc8249c
Binary files /dev/null and b/v2/internal/templates/templates/vuetify2-basic/frontend/src/assets/images/logo.png differ
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/src/components/HelloWorld.vue b/v2/internal/templates/templates/vuetify2-basic/frontend/src/components/HelloWorld.vue
new file mode 100644
index 000000000..6ba7a2791
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/frontend/src/components/HelloWorld.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+ Press Me
+
+
+
+
+
+
+
+
+ Message from Go
+ {{message}}
+
+
+
+ Awesome
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/src/main.js b/v2/internal/templates/templates/vuetify2-basic/frontend/src/main.js
new file mode 100644
index 000000000..1747f1893
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/frontend/src/main.js
@@ -0,0 +1,27 @@
+import 'core-js/stable';
+import 'regenerator-runtime/runtime';
+import '@mdi/font/css/materialdesignicons.css';
+import Vue from 'vue';
+import Vuetify from 'vuetify';
+import 'vuetify/dist/vuetify.min.css';
+
+Vue.use(Vuetify);
+
+import App from './App.vue';
+
+Vue.config.productionTip = false;
+Vue.config.devtools = true;
+
+Wails.Init(() => {
+ new Vue({
+ vuetify: new Vuetify({
+ icons: {
+ iconfont: 'mdi'
+ },
+ theme: {
+ dark: true
+ }
+ }),
+ render: h => h(App)
+ }).$mount('#app');
+});
diff --git a/v2/internal/templates/templates/vuetify2-basic/frontend/vue.config.js b/v2/internal/templates/templates/vuetify2-basic/frontend/vue.config.js
new file mode 100644
index 000000000..3ce2deead
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/frontend/vue.config.js
@@ -0,0 +1,60 @@
+let cssConfig = {};
+
+if (process.env.NODE_ENV == 'production') {
+ cssConfig = {
+ extract: {
+ filename: '[name].css',
+ chunkFilename: '[name].css'
+ }
+ };
+}
+
+module.exports = {
+ indexPath: 'index.html',
+ publicPath: 'public',
+ // disable hashes in filenames
+ filenameHashing: false,
+ // delete HTML related webpack plugins
+ chainWebpack: config => {
+ config.plugins.delete('preload');
+ config.plugins.delete('prefetch');
+ config
+ .plugin('html')
+ .tap(args => {
+ args[0].template = 'public/index.html';
+ args[0].inject = false;
+ args[0].cache = false;
+ args[0].minify = false;
+ args[0].filename = 'index.html';
+ return args;
+ });
+
+ let limit = 9999999999999999;
+ config.module
+ .rule('images')
+ .test(/\.(png|gif|jpg)(\?.*)?$/i)
+ .use('url-loader')
+ .loader('url-loader')
+ .tap(options => Object.assign(options, { limit: limit }));
+ config.module
+ .rule('fonts')
+ .test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i)
+ .use('url-loader')
+ .loader('url-loader')
+ .options({
+ limit: limit
+ });
+ },
+ css: cssConfig,
+ configureWebpack: {
+ output: {
+ filename: '[name].js'
+ },
+ optimization: {
+ splitChunks: false
+ }
+ },
+ devServer: {
+ disableHostCheck: true
+ }
+};
diff --git a/v2/internal/templates/templates/vuetify2-basic/go.mod.tmpl b/v2/internal/templates/templates/vuetify2-basic/go.mod.tmpl
new file mode 100644
index 000000000..21bd6c6a2
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/go.mod.tmpl
@@ -0,0 +1,9 @@
+module test
+
+go 1.13
+
+require (
+ github.com/wailsapp/wails/v2 v2.0.0-alpha
+)
+
+replace github.com/wailsapp/wails/v2 v2.0.0-alpha => {{.WailsDirectory}}
diff --git a/v2/internal/templates/templates/vuetify2-basic/main.tmpl.go b/v2/internal/templates/templates/vuetify2-basic/main.tmpl.go
new file mode 100644
index 000000000..5656d03e3
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/main.tmpl.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+ wails "github.com/wailsapp/wails/v2"
+)
+
+func main() {
+
+ // Create application with options
+ app := wails.CreateApp("{{.ProjectName}}", 1024, 768)
+
+ app.Bind(newBasic())
+
+ app.Run()
+}
diff --git a/v2/internal/templates/templates/vuetify2-basic/template.json b/v2/internal/templates/templates/vuetify2-basic/template.json
new file mode 100644
index 000000000..4ecb6b4b9
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/template.json
@@ -0,0 +1,7 @@
+{
+ "name": "Vuetify 2 ",
+ "shortname": "vuetify2",
+ "author": "Michael Hipp ",
+ "description": "A simple template using only HTML/CSS/JS",
+ "helpurl": "https://wails.app/help/templates/vuetify2"
+}
diff --git a/v2/internal/templates/templates/vuetify2-basic/wails.tmpl.json b/v2/internal/templates/templates/vuetify2-basic/wails.tmpl.json
new file mode 100644
index 000000000..05fd2f26a
--- /dev/null
+++ b/v2/internal/templates/templates/vuetify2-basic/wails.tmpl.json
@@ -0,0 +1,9 @@
+{
+ "name": "{{.ProjectName}}",
+ "outputfilename": "{{.BinaryName}}",
+ "html": "frontend/dist/index.html",
+ "js": "frontend/dist/app.js",
+ "css": "frontend/dist/app.css",
+ "frontend:build": "npm run build",
+ "frontend:install": "npm install"
+}
diff --git a/v2/internal/templates/templates_test.go b/v2/internal/templates/templates_test.go
new file mode 100644
index 000000000..60302defd
--- /dev/null
+++ b/v2/internal/templates/templates_test.go
@@ -0,0 +1,20 @@
+package templates
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/matryer/is"
+)
+
+func TestList(t *testing.T) {
+
+ is := is.New(t)
+ templates, err := List()
+ is.Equal(err, nil)
+
+ println("Found these templates:")
+ for _, template := range templates {
+ fmt.Printf("%+v\n", template)
+ }
+}
diff --git a/v2/internal/webserver/routes.go b/v2/internal/webserver/routes.go
new file mode 100644
index 000000000..3b65b2e79
--- /dev/null
+++ b/v2/internal/webserver/routes.go
@@ -0,0 +1,49 @@
+package webserver
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+)
+
+func (w *WebServer) setUpRoutes() error {
+ // Handle Index + assets
+ http.HandleFunc("/", w.serveAssets)
+
+ // Handle Bindings
+ http.HandleFunc("/bindings.js", w.serveBindings)
+
+ // Handle Wails
+ http.HandleFunc("/wails.js", w.serveWails)
+
+ // Handle websocket connection
+ http.HandleFunc("/ws", w.websocketConnection)
+
+ return nil
+}
+
+func (w *WebServer) serveAssets(resp http.ResponseWriter, req *http.Request) {
+ fileserver := http.FileServer(w.assets)
+ fileserver.ServeHTTP(resp, req)
+}
+
+func (w *WebServer) serveBindings(resp http.ResponseWriter, req *http.Request) {
+ resp.Header().Add("content-type", "application/javascript")
+ bindings, err := w.bindings.ToJSON()
+ if err != nil {
+ w.logger.Error("Failed to convert bindings to JSON: %v", err)
+ fmt.Fprintf(resp, "")
+ return
+ }
+ w.logger.Debug("Sending bindings to webclient: %v", bindings)
+ b, _ := json.Marshal(bindings)
+ fmt.Fprintf(resp, fmt.Sprintf("window.wailsbindings=%s; window.SetBindings(window.wailsbindings);", b))
+}
+
+func (w *WebServer) serveWails(resp http.ResponseWriter, req *http.Request) {
+ resp.Header().Add("content-type", "application/javascript")
+ if data, err := w.assets.String("/wails.js"); err == nil {
+ fmt.Fprintf(resp, strings.Replace(data, ":8080", fmt.Sprintf(":%d", w.port), 1))
+ }
+}
diff --git a/v2/internal/webserver/webassets.go b/v2/internal/webserver/webassets.go
new file mode 100644
index 000000000..9f73c8f77
--- /dev/null
+++ b/v2/internal/webserver/webassets.go
@@ -0,0 +1,9 @@
+package webserver
+
+import "github.com/wailsapp/wails/v2/internal/assetdb"
+
+var (
+ // WebAssets is our single asset db instance.
+ // It will be constructed by a dynamically generated method in this directory.
+ WebAssets *assetdb.AssetDB = assetdb.NewAssetDB()
+)
diff --git a/v2/internal/webserver/webserver.go b/v2/internal/webserver/webserver.go
new file mode 100644
index 000000000..4b143d250
--- /dev/null
+++ b/v2/internal/webserver/webserver.go
@@ -0,0 +1,87 @@
+package webserver
+
+import (
+ "fmt"
+ "net/http"
+ "sync"
+
+ "github.com/wailsapp/wails/v2/internal/assetdb"
+ "github.com/wailsapp/wails/v2/internal/binding"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/messagedispatcher"
+ "github.com/wailsapp/wails/v2/internal/subsystem"
+)
+
+// WebServer serves the application over http
+type WebServer struct {
+ port int
+ ip string
+ assets *assetdb.AssetDB
+ logger *logger.Logger
+ dispatcher *messagedispatcher.Dispatcher
+ event *subsystem.Event
+ server *http.Server
+ bindings *binding.Bindings
+
+ lock sync.Mutex
+ connections map[string]*WebClient
+}
+
+// NewWebServer creates a new WebServer
+func NewWebServer(logger *logger.Logger) *WebServer {
+
+ // Return a WebServer with default values
+ return &WebServer{
+ assets: db,
+ connections: make(map[string]*WebClient),
+ logger: logger,
+ }
+}
+
+// URL returns the URL that the server is serving from
+func (w *WebServer) URL() string {
+ return fmt.Sprintf("http://%s:%d", w.ip, w.port)
+}
+
+// SetPort sets the server port to listen on
+func (w *WebServer) SetPort(port int) {
+ w.port = port
+}
+
+// SetIP sets the server ip to listen on
+func (w *WebServer) SetIP(ip string) {
+ w.ip = ip
+}
+
+// SetBindings provides the webserver with the mapping of bindings to provide to a client
+func (w *WebServer) SetBindings(bindings *binding.Bindings) {
+ w.bindings = bindings
+}
+
+// Start the webserver
+func (w *WebServer) Start(dispatcher *messagedispatcher.Dispatcher, event *subsystem.Event) error {
+ var err error
+
+ // Create the server
+ w.server = &http.Server{Addr: fmt.Sprintf("%s:%d", w.ip, w.port)}
+ w.event = event
+
+ // Set up the Web Server's routes
+ err = w.setUpRoutes()
+ if err != nil {
+ return err
+ }
+
+ // Save the dispatcher
+ w.dispatcher = dispatcher
+
+ // Start the WebServer
+ err = w.server.ListenAndServe()
+
+ // Return any error except http.ErrServerClosed
+ if err != nil && err != http.ErrServerClosed {
+ return err
+ }
+
+ return nil
+}
diff --git a/v2/internal/webserver/websockets.go b/v2/internal/webserver/websockets.go
new file mode 100644
index 000000000..004d23157
--- /dev/null
+++ b/v2/internal/webserver/websockets.go
@@ -0,0 +1,137 @@
+package webserver
+
+import (
+ "context"
+ "net/http"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/logger"
+ ws "nhooyr.io/websocket"
+ "nhooyr.io/websocket/wsjson"
+)
+
+// WebClient represents an individual web session
+type WebClient struct {
+ conn *ws.Conn
+ identifier string
+ logger *logger.Logger
+ running bool
+}
+
+// Quit terminates the webclient session
+func (wc *WebClient) Quit() {
+ wc.running = false
+}
+
+// NotifyEvent sends the event
+func (wc *WebClient) NotifyEvent(message string) {
+ wc.SendMessage("E" + message)
+}
+
+// CallResult sends the result of the Go function call back to the
+// originator in the frontend
+func (wc *WebClient) CallResult(message string) {
+ wc.SendMessage("R" + message)
+}
+
+// SaveFileDialog is a noop in the webclient
+func (wc *WebClient) SaveFileDialog(title string) string {
+ return ""
+}
+
+// OpenFileDialog is a noop in the webclient
+func (wc *WebClient) OpenFileDialog(title string) string {
+ return ""
+}
+
+// OpenDirectoryDialog is a noop in the webclient
+func (wc *WebClient) OpenDirectoryDialog(title string) string {
+ return ""
+}
+
+// WindowSetTitle is a noop in the webclient
+func (wc *WebClient) WindowSetTitle(title string) {}
+
+// WindowFullscreen is a noop in the webclient
+func (wc *WebClient) WindowFullscreen() {}
+
+// WindowUnFullscreen is a noop in the webclient
+func (wc *WebClient) WindowUnFullscreen() {}
+
+// WindowSetColour is a noop in the webclient
+func (wc *WebClient) WindowSetColour(colour string) bool {
+ return false
+}
+
+// Run processes messages from the remote webclient
+func (wc *WebClient) Run(w *WebServer) {
+ dispatcher := w.dispatcher.RegisterClient(wc)
+ defer w.dispatcher.RemoveClient(dispatcher)
+ defer w.unregisterClient(wc.identifier)
+
+ for wc.running {
+ var v interface{}
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ if err := wsjson.Read(ctx, wc.conn, &v); err != nil {
+ if ws.CloseStatus(err) == ws.StatusNormalClosure || ws.CloseStatus(err) == ws.StatusGoingAway {
+ break
+ }
+ if ws.CloseStatus(err) != -1 {
+ w.logger.Debug("Connection error: %s - %s", wc.identifier, err)
+ break
+ }
+
+ if !strings.Contains(err.Error(), "status = Status") {
+ w.logger.Debug("Error encountered on socket: %v", err)
+ break
+ }
+ }
+ dispatcher.DispatchMessage(v.(string))
+ }
+
+ wc.conn.Close(ws.StatusNormalClosure, "Goodbye")
+ w.logger.Debug("Connection closed: %v", wc.identifier)
+}
+
+// SendMessage converts the string to a []byte and passes it to
+// the connection's Writer to send to the remote client
+// The Writer itself prevents multiple users at the same time.
+func (wc *WebClient) SendMessage(message string) {
+ wc.logger.Debug("WebClient.SendMessage() - %s", message)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ wc.conn.Write(ctx, ws.MessageText, []byte(message))
+}
+
+// unregisterClient is called automatically by a WebClient session during termination
+// so that it's registration can be removed
+func (w *WebServer) unregisterClient(identifier string) {
+ w.logger.Debug("Removing WebClient : %v", identifier)
+ w.lock.Lock()
+ delete(w.connections, identifier)
+ w.lock.Unlock()
+}
+
+func (w *WebServer) websocketConnection(resp http.ResponseWriter, req *http.Request) {
+ conn, err := ws.Accept(resp, req, nil)
+ if err != nil {
+ w.logger.Debug("Failed to upgrade websocket connection")
+ return
+ }
+ wc := &WebClient{
+ conn: conn,
+ identifier: req.RemoteAddr,
+ logger: w.logger,
+ running: true,
+ }
+ w.lock.Lock()
+ w.connections[wc.identifier] = wc
+ w.lock.Unlock()
+
+ w.logger.Debug("Connection from: %v", wc.identifier)
+
+ go wc.Run(w)
+
+}
diff --git a/v2/pkg/commands/build/base.go b/v2/pkg/commands/build/base.go
new file mode 100644
index 000000000..9c1707d2a
--- /dev/null
+++ b/v2/pkg/commands/build/base.go
@@ -0,0 +1,351 @@
+package build
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ "github.com/leaanthony/slicer"
+ "github.com/wailsapp/wails/v2/internal/assetdb"
+ "github.com/wailsapp/wails/v2/internal/fs"
+ "github.com/wailsapp/wails/v2/internal/html"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/project"
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+// BaseBuilder is the common builder struct
+type BaseBuilder struct {
+ filesToDelete slicer.StringSlicer
+ projectData *project.Project
+}
+
+// NewBaseBuilder creates a new BaseBuilder
+func NewBaseBuilder() *BaseBuilder {
+ result := &BaseBuilder{}
+ return result
+}
+
+// SetProjectData sets the project data for this builder
+func (b *BaseBuilder) SetProjectData(projectData *project.Project) {
+ b.projectData = projectData
+}
+
+func (b *BaseBuilder) addFileToDelete(filename string) {
+ b.filesToDelete.Add(filename)
+}
+
+func (b *BaseBuilder) fileExists(path string) bool {
+ // if file doesn't exist, ignore
+ _, err := os.Stat(path)
+ if err != nil {
+ return !os.IsNotExist(err)
+ }
+ return true
+}
+
+// buildStaticAssets will iterate through the projects static directory and add all files
+// to the application wide asset database.
+func (b *BaseBuilder) buildStaticAssets(projectData *project.Project) error {
+
+ // Add trailing slash to Asset directory
+ assetsDir := filepath.Join(projectData.Path, "assets") + "/"
+
+ assets := assetdb.NewAssetDB()
+ if b.fileExists(assetsDir) {
+ err := filepath.Walk(assetsDir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ normalisedPath := filepath.ToSlash(path)
+ localPath := strings.TrimPrefix(normalisedPath, assetsDir)
+ if len(localPath) == 0 {
+ return nil
+ }
+ if data, err := ioutil.ReadFile(filepath.Join(assetsDir, localPath)); err == nil {
+ assets.AddAsset(localPath, data)
+ }
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ }
+
+ // Write assetdb out to root directory
+ assetsDbFilename := fs.RelativePath("../../../assetsdb.go")
+ b.addFileToDelete(assetsDbFilename)
+ err := ioutil.WriteFile(assetsDbFilename, []byte(assets.Serialize("assets", "wails")), 0644)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (b *BaseBuilder) convertFileToIntegerString(filename string) (string, error) {
+ rawData, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return "", err
+ }
+ return b.convertByteSliceToIntegerString(rawData), nil
+}
+
+func (b *BaseBuilder) convertByteSliceToIntegerString(data []byte) string {
+
+ // Create string builder
+ var result strings.Builder
+
+ if len(data) > 0 {
+
+ // Loop over all but 1 bytes
+ for i := 0; i < len(data)-1; i++ {
+ result.WriteString(fmt.Sprintf("%v,", data[i]))
+ }
+
+ result.WriteString(fmt.Sprintf("%v", data[len(data)-1]))
+
+ }
+
+ return result.String()
+}
+
+// CleanUp does post-build housekeeping
+func (b *BaseBuilder) CleanUp() {
+
+ // Delete all the files
+ b.filesToDelete.Each(func(filename string) {
+
+ // if file doesn't exist, ignore
+ if !b.fileExists(filename) {
+ return
+ }
+
+ // Delete file. We ignore errors because these files will be overwritten
+ // by the next build anyway.
+ os.Remove(filename)
+
+ })
+}
+
+// CompileProject compiles the project
+func (b *BaseBuilder) CompileProject(options *Options) error {
+
+ // Default go build command
+ commands := slicer.String([]string{"build"})
+
+ var tags slicer.StringSlicer
+ tags.Add(options.OutputType)
+
+ if options.Mode == Debug {
+ tags.Add("debug")
+ }
+
+ // Add the output type build tag
+ commands.Add("-tags")
+ commands.Add(tags.Join(","))
+
+ // Strip binary in Production mode
+ if options.Mode == Production {
+
+ // Different linker flags depending on platform
+ commands.Add("-ldflags")
+
+ switch runtime.GOOS {
+ case "windows":
+ commands.Add("-w -s -H windowsgui")
+ default:
+ commands.Add("-w -s")
+ }
+ }
+
+ // Get application build directory
+ appDir, err := getApplicationBuildDirectory(options, options.Platform)
+ if err != nil {
+ return err
+ }
+
+ if options.LDFlags != "" {
+ commands.Add("-ldflags")
+ commands.Add(options.LDFlags)
+ }
+
+ // Set up output filename
+ outputFile := options.OutputFile
+ if outputFile == "" {
+ outputFile = b.projectData.OutputFilename
+ }
+ outputFilePath := filepath.Join(appDir, outputFile)
+ commands.Add("-o")
+ commands.Add(outputFilePath)
+
+ b.projectData.OutputFilename = strings.TrimPrefix(outputFilePath, options.ProjectData.Path)
+
+ // Create the command
+ cmd := exec.Command(options.Compiler, commands.AsSlice()...)
+
+ // Set the directory
+ cmd.Dir = b.projectData.Path
+
+ // Set GO111MODULE environment variable
+ cmd.Env = append(os.Environ(), "GO111MODULE=on")
+
+ // Setup buffers
+ var stdo, stde bytes.Buffer
+ cmd.Stdout = &stdo
+ cmd.Stderr = &stde
+
+ // Run command
+ err = cmd.Run()
+
+ // Format error if we have one
+ if err != nil {
+ return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
+ }
+
+ return nil
+}
+
+// NpmInstall runs "npm install" in the given directory
+func (b *BaseBuilder) NpmInstall(sourceDir string) error {
+ return b.NpmInstallUsingCommand(sourceDir, "npm install")
+}
+
+// NpmInstallUsingCommand runs the given install command in the specified npm project directory
+func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand string) error {
+
+ packageJSON := filepath.Join(sourceDir, "package.json")
+
+ // Check package.json exists
+ if !fs.FileExists(packageJSON) {
+ return fmt.Errorf("unable to load package.json at '%s'", packageJSON)
+ }
+
+ install := false
+
+ // Get the MD5 sum of package.json
+ packageJSONMD5 := fs.MustMD5File(packageJSON)
+
+ // Check whether we need to npm install
+ packageChecksumFile := filepath.Join(sourceDir, "package.json.md5")
+ if fs.FileExists(packageChecksumFile) {
+ // Compare checksums
+ storedChecksum := fs.MustLoadString(packageChecksumFile)
+ if storedChecksum != packageJSONMD5 {
+ fs.MustWriteString(packageChecksumFile, packageJSONMD5)
+ install = true
+ }
+ } else {
+ install = true
+ fs.MustWriteString(packageChecksumFile, packageJSONMD5)
+ }
+
+ // Install if node_modules doesn't exist
+ nodeModulesDir := filepath.Join(sourceDir, "node_modules")
+ if !fs.DirExists(nodeModulesDir) {
+ install = true
+ }
+
+ // Shortcut installation
+ if install == false {
+ return nil
+ }
+
+ // Split up the InstallCommand and execute it
+ cmd := strings.Split(installCommand, " ")
+ stdout, stderr, err := shell.RunCommand(sourceDir, cmd[0], cmd[1:]...)
+ if err != nil {
+ for _, l := range strings.Split(stdout, "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ for _, l := range strings.Split(stderr, "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ }
+
+ return err
+}
+
+// NpmRun executes the npm target in the provided directory
+func (b *BaseBuilder) NpmRun(projectDir, buildTarget string, verbose bool) error {
+ stdout, stderr, err := shell.RunCommand(projectDir, "npm", "run", buildTarget)
+ if verbose || err != nil {
+ for _, l := range strings.Split(stdout, "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ for _, l := range strings.Split(stderr, "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ }
+ return err
+}
+
+// NpmRunWithEnvironment executes the npm target in the provided directory, with the given environment variables
+func (b *BaseBuilder) NpmRunWithEnvironment(projectDir, buildTarget string, verbose bool, envvars []string) error {
+ cmd := shell.CreateCommand(projectDir, "npm", "run", buildTarget)
+ cmd.Env = append(os.Environ(), envvars...)
+ var stdo, stde bytes.Buffer
+ cmd.Stdout = &stdo
+ cmd.Stderr = &stde
+ err := cmd.Run()
+ if verbose || err != nil {
+ for _, l := range strings.Split(stdo.String(), "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ for _, l := range strings.Split(stde.String(), "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ }
+ return err
+}
+
+// BuildFrontend executes the `npm build` command for the frontend directory
+func (b *BaseBuilder) BuildFrontend(outputLogger *logger.Logger) error {
+ verbose := false
+
+ frontendDir := filepath.Join(b.projectData.Path, "frontend")
+
+ // Check there is an 'InstallCommand' provided in wails.json
+ if b.projectData.InstallCommand == "" {
+ // No - don't install
+ outputLogger.Writeln(" - No Install command. Skipping.")
+ } else {
+ // Do install if needed
+ outputLogger.Writeln(" - Installing dependencies...")
+ if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand); err != nil {
+ return err
+ }
+ }
+
+ // Check if there is a build command
+ if b.projectData.BuildCommand == "" {
+ outputLogger.Writeln(" - No Build command. Skipping.")
+ // No - ignore
+ return nil
+ }
+
+ outputLogger.Writeln(" - Compiling Frontend Project")
+ cmd := strings.Split(b.projectData.BuildCommand, " ")
+ stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
+ if verbose || err != nil {
+ for _, l := range strings.Split(stdout, "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ for _, l := range strings.Split(stderr, "\n") {
+ fmt.Printf(" %s\n", l)
+ }
+ }
+ return err
+}
+
+// ExtractAssets gets the assets from the index.html file
+func (b *BaseBuilder) ExtractAssets() (*html.AssetBundle, error) {
+
+ // Read in html
+ return html.NewAssetBundle(b.projectData.HTML)
+}
diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go
new file mode 100644
index 000000000..20c6e3879
--- /dev/null
+++ b/v2/pkg/commands/build/build.go
@@ -0,0 +1,137 @@
+package build
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+
+ "github.com/leaanthony/slicer"
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/project"
+)
+
+// Mode is the type used to indicate the build modes
+type Mode int
+
+const (
+ // Debug mode
+ Debug Mode = iota
+ // Production mode
+ Production
+)
+
+var modeMap = []string{"Debug", "Production"}
+
+// Options contains all the build options as well as the project data
+type Options struct {
+ LDFlags string // Optional flags to pass to linker
+ Logger *logger.Logger // All output to the logger
+ OutputType string // EG: desktop, server....
+ Mode Mode // release or debug
+ ProjectData *project.Project // The project data
+ Pack bool // Create a package for the app after building
+ Platform string // The platform to build for
+ Compiler string // The compiler command to use
+ IgnoreFrontend bool // Indicates if the frontend does not need building
+ OutputFile string // Override the output filename
+}
+
+// GetModeAsString returns the current mode as a string
+func GetModeAsString(mode Mode) string {
+ return modeMap[mode]
+}
+
+// Build the project!
+func Build(options *Options) (string, error) {
+
+ // Extract logger
+ outputLogger := options.Logger
+
+ // Create a default logger if it doesn't exist
+ if outputLogger == nil {
+ outputLogger = logger.New()
+ }
+
+ // Get working directory
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+
+ // Check platform
+ validPlatforms := slicer.String([]string{"linux"})
+ if !validPlatforms.Contains(options.Platform) {
+ return "", fmt.Errorf("platform %s not supported", options.Platform)
+ }
+
+ // Load project
+ projectData, err := project.Load(cwd)
+ if err != nil {
+ return "", err
+ }
+ options.ProjectData = projectData
+
+ // Save the project type
+ projectData.OutputType = options.OutputType
+
+ // Create builder
+ var builder Builder
+
+ switch projectData.OutputType {
+ case "desktop":
+ builder = newDesktopBuilder()
+ case "hybrid":
+ builder = newHybridBuilder()
+ case "server":
+ builder = newServerBuilder()
+ default:
+ return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
+ }
+
+ // Set up our clean up method
+ defer builder.CleanUp()
+
+ // Initialise Builder
+ builder.SetProjectData(projectData)
+
+ if !options.IgnoreFrontend {
+ outputLogger.Writeln(" - Building Wails Frontend")
+ err = builder.BuildFrontend(outputLogger)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ // Build the base assets
+ outputLogger.Writeln(" - Compiling Assets")
+ err = builder.BuildRuntime(options)
+ if err != nil {
+ return "", err
+ }
+ err = builder.BuildAssets(options)
+ if err != nil {
+ return "", err
+ }
+
+ // Compile the application
+ outputLogger.Write(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
+ err = builder.CompileProject(options)
+ if err != nil {
+ return "", err
+ }
+ outputLogger.Writeln("done.")
+ // Do we need to pack the app?
+ if options.Pack {
+
+ outputLogger.Writeln(" - Packaging Application")
+
+ // TODO: Allow cross platform build
+ err = packageProject(options, runtime.GOOS)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ return projectData.OutputFilename, nil
+
+}
diff --git a/v2/pkg/commands/build/builder.go b/v2/pkg/commands/build/builder.go
new file mode 100644
index 000000000..15b5f08f6
--- /dev/null
+++ b/v2/pkg/commands/build/builder.go
@@ -0,0 +1,16 @@
+package build
+
+import (
+ "github.com/wailsapp/wails/v2/internal/logger"
+ "github.com/wailsapp/wails/v2/internal/project"
+)
+
+// Builder defines a builder that can build Wails applications
+type Builder interface {
+ SetProjectData(projectData *project.Project)
+ BuildAssets(*Options) error
+ BuildFrontend(*logger.Logger) error
+ BuildRuntime(*Options) error
+ CompileProject(*Options) error
+ CleanUp()
+}
diff --git a/v2/pkg/commands/build/desktop.go b/v2/pkg/commands/build/desktop.go
new file mode 100644
index 000000000..897acbe73
--- /dev/null
+++ b/v2/pkg/commands/build/desktop.go
@@ -0,0 +1,136 @@
+package build
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+
+ "github.com/wailsapp/wails/v2/internal/fs"
+ "github.com/wailsapp/wails/v2/internal/html"
+ "github.com/wailsapp/wails/v2/internal/logger"
+)
+
+// DesktopBuilder builds applications for the desktop
+type DesktopBuilder struct {
+ *BaseBuilder
+}
+
+func newDesktopBuilder() *DesktopBuilder {
+ return &DesktopBuilder{
+ BaseBuilder: NewBaseBuilder(),
+ }
+}
+
+// BuildAssets builds the assets for the desktop application
+func (d *DesktopBuilder) BuildAssets(options *Options) error {
+ var err error
+
+ // Get a list of assets from the HTML
+ assets, err := d.BaseBuilder.ExtractAssets()
+ if err != nil {
+ return err
+ }
+
+ // Build base assets (HTML/JS/CSS/etc)
+ err = d.BuildBaseAssets(assets, options.Logger)
+ if err != nil {
+ return err
+ }
+
+ // Build static assets
+ err = d.buildStaticAssets(d.projectData)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// BuildBaseAssets builds the assets for the desktop application
+func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, outputLogger *logger.Logger) error {
+ var err error
+
+ outputLogger.Write(" - Embedding Assets...")
+
+ // Get target asset directory
+ assetDir := fs.RelativePath("../../../internal/ffenestri")
+
+ // Dump assets as C
+ assetsFile, err := assets.WriteToCFile(assetDir)
+ if err != nil {
+ return err
+ }
+ d.addFileToDelete(assetsFile)
+
+ // Process Icon
+ err = d.processIcon(assetDir)
+ if err != nil {
+ return err
+ }
+
+ outputLogger.Writeln("done.")
+
+ return nil
+}
+
+// processIcon will copy a default icon if one doesn't exist, then, if
+// needed, will compile the icon
+func (d *DesktopBuilder) processIcon(assetDir string) error {
+
+ // Copy default icon if one doesn't exist
+ iconFile := filepath.Join(d.projectData.Path, "icon.png")
+ if !fs.FileExists(iconFile) {
+ err := fs.CopyFile(defaultIconPath(), iconFile)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Compile Icon
+ return d.compileIcon(assetDir, iconFile)
+}
+
+// BuildRuntime builds the Wails javascript runtime and then converts it into a C file
+func (d *DesktopBuilder) BuildRuntime(options *Options) error {
+
+ outputLogger := options.Logger
+
+ sourceDir := fs.RelativePath("../../../internal/runtime/js")
+
+ if err := d.NpmInstall(sourceDir); err != nil {
+ return err
+ }
+
+ outputLogger.Write(" - Embedding Runtime...")
+ envvars := []string{"WAILSPLATFORM=" + options.Platform}
+ if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil {
+ return err
+ }
+
+ wailsJS := fs.RelativePath("../../../internal/runtime/assets/desktop.js")
+ runtimeData, err := ioutil.ReadFile(wailsJS)
+ if err != nil {
+ return err
+ }
+ outputLogger.Writeln("done.")
+
+ // Convert to C structure
+ runtimeC := `
+// runtime.c (c) 2019-Present Lea Anthony.
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file was auto-generated. DO NOT MODIFY.
+const unsigned char runtime[]={`
+ for _, b := range runtimeData {
+ runtimeC += fmt.Sprintf("0x%x, ", b)
+ }
+ runtimeC += "0x00};"
+
+ // Save file
+ outputFile := fs.RelativePath("../../../internal/ffenestri/runtime.c")
+
+ if err := ioutil.WriteFile(outputFile, []byte(runtimeC), 0600); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/v2/pkg/commands/build/desktop_default.go b/v2/pkg/commands/build/desktop_default.go
new file mode 100644
index 000000000..0418001ff
--- /dev/null
+++ b/v2/pkg/commands/build/desktop_default.go
@@ -0,0 +1,8 @@
+// +build !linux
+
+package build
+
+// This is used when there is no compilation to be done for the asset
+func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error {
+ return nil
+}
diff --git a/v2/pkg/commands/build/desktop_linux.go b/v2/pkg/commands/build/desktop_linux.go
new file mode 100644
index 000000000..2c8fee122
--- /dev/null
+++ b/v2/pkg/commands/build/desktop_linux.go
@@ -0,0 +1,54 @@
+// +build linux
+
+package build
+
+import (
+ "image/png"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/xyproto/xpm"
+)
+
+// compileIcon will compile the icon found at /icon.png into the application
+func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error {
+
+ // Load icon into a databuffer
+ targetFilename := "icon"
+ targetFile := filepath.Join(assetDir, targetFilename+".c")
+
+ d.addFileToDelete(targetFile)
+
+ // Create a new XPM encoder
+ enc := xpm.NewEncoder(targetFilename)
+
+ // Open the PNG file
+ f, err := os.Open(iconFile)
+ if err != nil {
+ return err
+ }
+ m, err := png.Decode(f)
+ if err != nil {
+ return err
+ }
+ f.Close()
+
+ var buf strings.Builder
+
+ // Generate and output the XPM data
+ err = enc.Encode(&buf, m)
+ if err != nil {
+ return err
+ }
+
+ // Massage the output so we can extern reference it
+ output := buf.String()
+ output = strings.Replace(output, "static char", "const char", 1)
+
+ // save icon.c
+ err = ioutil.WriteFile(targetFile, []byte(output), 0755)
+
+ return err
+}
diff --git a/v2/pkg/commands/build/hybrid.go b/v2/pkg/commands/build/hybrid.go
new file mode 100644
index 000000000..a7e2e0cbd
--- /dev/null
+++ b/v2/pkg/commands/build/hybrid.go
@@ -0,0 +1,101 @@
+package build
+
+import (
+ "github.com/wailsapp/wails/v2/internal/project"
+)
+
+// HybridBuilder builds applications as a server
+type HybridBuilder struct {
+ *BaseBuilder
+ desktop *DesktopBuilder
+ server *ServerBuilder
+}
+
+func newHybridBuilder() Builder {
+ result := &HybridBuilder{
+ BaseBuilder: NewBaseBuilder(),
+ desktop: newDesktopBuilder(),
+ server: newServerBuilder(),
+ }
+ return result
+}
+
+// BuildAssets builds the assets for the desktop application
+func (b *HybridBuilder) BuildAssets(options *Options) error {
+ var err error
+
+ // Build base assets (HTML/JS/CSS/etc)
+ err = b.BuildBaseAssets(options)
+ if err != nil {
+ return err
+ }
+ // Build static assets
+ err = b.buildStaticAssets(b.projectData)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// BuildAssets builds the assets for the desktop application
+func (b *HybridBuilder) BuildBaseAssets(options *Options) error {
+
+ assets, err := b.BaseBuilder.ExtractAssets()
+ if err != nil {
+ return err
+ }
+
+ err = b.desktop.BuildBaseAssets(assets, options.Logger)
+ if err != nil {
+ return err
+ }
+
+ err = b.server.BuildBaseAssets(assets)
+ if err != nil {
+ return err
+ }
+
+ // Build desktop static assets
+ err = b.desktop.buildStaticAssets(b.projectData)
+ if err != nil {
+ return err
+ }
+
+ // Build server static assets
+ err = b.server.buildStaticAssets(b.projectData)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (b *HybridBuilder) BuildRuntime(options *Options) error {
+ err := b.desktop.BuildRuntime(options)
+ if err != nil {
+ return err
+ }
+
+ err = b.server.BuildRuntime(options)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (b *HybridBuilder) SetProjectData(projectData *project.Project) {
+ b.BaseBuilder.SetProjectData(projectData)
+ b.desktop.SetProjectData(projectData)
+ b.server.SetProjectData(projectData)
+}
+
+func (b *HybridBuilder) CompileProject(options *Options) error {
+ return b.BaseBuilder.CompileProject(options)
+}
+
+func (b *HybridBuilder) CleanUp() {
+ b.desktop.CleanUp()
+ b.server.CleanUp()
+}
diff --git a/v2/pkg/commands/build/internal/packager/icon1024.png b/v2/pkg/commands/build/internal/packager/icon1024.png
new file mode 100644
index 000000000..a3ad26ce7
Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon1024.png differ
diff --git a/v2/pkg/commands/build/internal/packager/icon128.png b/v2/pkg/commands/build/internal/packager/icon128.png
new file mode 100644
index 000000000..2ddf98679
Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon128.png differ
diff --git a/v2/pkg/commands/build/internal/packager/icon256.png b/v2/pkg/commands/build/internal/packager/icon256.png
new file mode 100644
index 000000000..0f4235b84
Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon256.png differ
diff --git a/v2/pkg/commands/build/internal/packager/icon32.png b/v2/pkg/commands/build/internal/packager/icon32.png
new file mode 100644
index 000000000..a03607d66
Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon32.png differ
diff --git a/v2/pkg/commands/build/internal/packager/icon512.png b/v2/pkg/commands/build/internal/packager/icon512.png
new file mode 100644
index 000000000..53c612c7b
Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon512.png differ
diff --git a/v2/pkg/commands/build/internal/packager/icon64.png b/v2/pkg/commands/build/internal/packager/icon64.png
new file mode 100644
index 000000000..a2b304154
Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon64.png differ
diff --git a/v2/pkg/commands/build/internal/packager/linux/AppRun b/v2/pkg/commands/build/internal/packager/linux/AppRun
new file mode 100644
index 000000000..621fed611
--- /dev/null
+++ b/v2/pkg/commands/build/internal/packager/linux/AppRun
@@ -0,0 +1,3 @@
+#!/bin/sh
+cd "$(dirname "$0")"
+exec ./{{.OutputFilename}}
\ No newline at end of file
diff --git a/v2/pkg/commands/build/internal/packager/linux/app.desktop b/v2/pkg/commands/build/internal/packager/linux/app.desktop
new file mode 100644
index 000000000..59f0456d2
--- /dev/null
+++ b/v2/pkg/commands/build/internal/packager/linux/app.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Application
+Exec={{.Name}}
+Name={{.Name}}
+Icon={{.Name}}
+Categories=Utility;
\ No newline at end of file
diff --git a/v2/pkg/commands/build/packager.go b/v2/pkg/commands/build/packager.go
new file mode 100644
index 000000000..76bc847de
--- /dev/null
+++ b/v2/pkg/commands/build/packager.go
@@ -0,0 +1,73 @@
+package build
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+
+ "github.com/wailsapp/wails/v2/internal/fs"
+)
+
+// PackageProject packages the application
+func packageProject(options *Options, platform string) error {
+
+ var err error
+ switch platform {
+ case "linux":
+ err = packageLinuxApplication(options)
+ default:
+ err = fmt.Errorf("packing not supported for %s yet", platform)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Gets (and creates) the platform/target build directory
+func getApplicationBuildDirectory(options *Options, platform string) (string, error) {
+ buildDirectory := filepath.Join(options.ProjectData.Path, "build", platform, options.OutputType)
+
+ // Clear out old builds
+ if fs.DirExists(buildDirectory) {
+ err := os.RemoveAll(buildDirectory)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ // Create clean directory
+ err := os.MkdirAll(buildDirectory, 0700)
+ if err != nil {
+ return "", err
+ }
+
+ return buildDirectory, nil
+}
+
+func copyFileToBuildDirectory() {}
+
+// Gets (and creates) the build base directory
+func getBuildBaseDirectory(options *Options) (string, error) {
+ buildDirectory := filepath.Join(options.ProjectData.Path, "build")
+ if !fs.DirExists(buildDirectory) {
+ err := os.MkdirAll(buildDirectory, 0700)
+ if err != nil {
+ return "", err
+ }
+ }
+ return buildDirectory, nil
+}
+
+// Gets the path to the default icon
+func defaultIconPath() string {
+ return fs.RelativePath("internal/packager/icon64.png")
+}
+
+// Gets the platform dependent package assets directory
+func getPackageAssetsDirectory() string {
+ return fs.RelativePath("internal/packager", runtime.GOOS)
+}
diff --git a/v2/pkg/commands/build/packager_linux.go b/v2/pkg/commands/build/packager_linux.go
new file mode 100644
index 000000000..55f8271c6
--- /dev/null
+++ b/v2/pkg/commands/build/packager_linux.go
@@ -0,0 +1,134 @@
+package build
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/leaanthony/slicer"
+ "github.com/wailsapp/wails/v2/internal/fs"
+ "github.com/wailsapp/wails/v2/internal/shell"
+)
+
+func deleteLinuxPackFiles(appDirBase string) {
+ // Delete appdir
+ appDir := filepath.Join(appDirBase, "AppDir")
+ os.RemoveAll(appDir)
+}
+
+func packageLinuxApplication(options *Options) error {
+
+ // Check we have AppImage tools
+
+ // Create AppImage build directory
+ buildDirectory, err := getApplicationBuildDirectory(options, "linux")
+ if err != nil {
+ return err
+ }
+
+ defer deleteLinuxPackFiles(buildDirectory)
+
+ // Get the name of the application and ensure we lower+kebab case it
+ name := filepath.Base(options.ProjectData.OutputFilename)
+
+ // Calculate asset directory
+ assetDir := getPackageAssetsDirectory()
+
+ // Copy default icon if one doesn't exist
+ baseBuildDirectory, err := getBuildBaseDirectory(options)
+ if err != nil {
+ return err
+ }
+ iconFile := filepath.Join(baseBuildDirectory, "icon.png")
+ if !fs.FileExists(iconFile) {
+ err = fs.CopyFile(defaultIconPath(), iconFile)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Copy Icon
+ targetIcon := filepath.Join(buildDirectory, name+".png")
+ err = fs.CopyFile(iconFile, targetIcon)
+ if err != nil {
+ return err
+ }
+
+ // Copy app.desktop
+ dotDesktopFile := filepath.Join(baseBuildDirectory, "linux", name+".desktop")
+ if !fs.FileExists(dotDesktopFile) {
+ bytes, err := ioutil.ReadFile(filepath.Join(assetDir, "app.desktop"))
+ if err != nil {
+ return err
+ }
+ appDesktop := string(bytes)
+ appDesktop = strings.ReplaceAll(appDesktop, `{{.Name}}`, name)
+ err = ioutil.WriteFile(dotDesktopFile, []byte(appDesktop), 0644)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Copy AppRun file
+ // targetFilename = filepath.Join(buildDirectory, "AppRun")
+ // if !fs.FileExists(targetFilename) {
+ // bytes, err := ioutil.ReadFile(filepath.Join(assetDir, "AppRun"))
+ // if err != nil {
+ // return err
+ // }
+ // appRun := string(bytes)
+ // appRun = strings.ReplaceAll(appRun, `{{.OutputFilename}}`, name)
+
+ // err = ioutil.WriteFile(targetFilename, []byte(appRun), 0644)
+ // if err != nil {
+ // return err
+ // }
+ // }
+
+ // Copy Binary
+ sourceFile := filepath.Join(options.ProjectData.Path, options.ProjectData.OutputFilename)
+ targetFile := filepath.Join(buildDirectory, options.ProjectData.OutputFilename)
+ err = fs.CopyFile(sourceFile, targetFile)
+ if err != nil {
+ return err
+ }
+ err = os.Chmod(targetFile, 0777)
+ if err != nil {
+ return err
+ }
+
+ /** Pack App **/
+
+ // Make file executable
+ // Set environment variable: OUTPUT=outputfilename
+ command := shell.NewCommand("linuxdeploy-x86_64.AppImage")
+ command.Dir(buildDirectory)
+
+ argslice := slicer.String()
+ argslice.Add("--appdir", "AppDir")
+ argslice.Add("-d", filepath.Join("..", name+".desktop"))
+ argslice.Add("-i", name+".png")
+ argslice.Add("-e", name)
+ argslice.Add("--output", "appimage")
+ command.AddArgs(argslice.AsSlice())
+
+ command.Env("OUTPUT", name+".AppImage")
+
+ err = command.Run()
+ if err != nil {
+ println(command.Stdout())
+ println(command.Stderr())
+ return err
+ }
+
+ // Copy app to project dir
+
+ println(buildDirectory)
+
+ return nil
+}
+
+func deleteDirectory(directory string) {
+ os.RemoveAll(directory)
+}
diff --git a/v2/pkg/commands/build/server-assetdb.go.template b/v2/pkg/commands/build/server-assetdb.go.template
new file mode 100644
index 000000000..f4720fee4
--- /dev/null
+++ b/v2/pkg/commands/build/server-assetdb.go.template
@@ -0,0 +1,11 @@
+package webserver
+
+// This sets up the assets that are defined in `webassets.go`
+func init() {
+ html = []byte{$HTMLBYTES$}
+ js = []byte{$JSBYTES$}
+ css = []byte{$CSSBYTES$}
+ wailsjs = []byte{$WAILSJSBYTES$}
+ jsurl = []byte{$JSURLBYTES$}
+ cssurl = []byte{$CSSURLBYTES$}
+}
\ No newline at end of file
diff --git a/v2/pkg/commands/build/server.go b/v2/pkg/commands/build/server.go
new file mode 100644
index 000000000..3b0245469
--- /dev/null
+++ b/v2/pkg/commands/build/server.go
@@ -0,0 +1,111 @@
+package build
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "github.com/wailsapp/wails/v2/internal/fs"
+ "github.com/wailsapp/wails/v2/internal/html"
+)
+
+// ServerBuilder builds applications as a server
+type ServerBuilder struct {
+ *BaseBuilder
+}
+
+func newServerBuilder() *ServerBuilder {
+ result := &ServerBuilder{
+ BaseBuilder: NewBaseBuilder(),
+ }
+ return result
+}
+
+// BuildAssets builds the assets for the desktop application
+func (s *ServerBuilder) BuildAssets(options *Options) error {
+ var err error
+
+ assets, err := s.BaseBuilder.ExtractAssets()
+ if err != nil {
+ return err
+ }
+
+ // Build embedded assets (HTML/JS/CSS/etc)
+ err = s.BuildBaseAssets(assets)
+ if err != nil {
+ return err
+ }
+
+ // Build static assets
+ err = s.buildStaticAssets(s.projectData)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// BuildBaseAssets builds the base assets
+func (s *ServerBuilder) BuildBaseAssets(assets *html.AssetBundle) error {
+ db, err := assets.ConvertToAssetDB()
+ if err != nil {
+ return err
+ }
+
+ // Fetch, update, and reinject index.html
+ index, err := db.Read("index.html")
+ if err != nil {
+ return fmt.Errorf(`Failed to locate "index.html"`)
+ }
+ splits := strings.Split(string(index), "
+
+
+
+
+
+
+
+
+")
+ if len(splits) != 2 {
+ return fmt.Errorf("Unable to locate a tag in your frontend/index.html")
+ }
+ injectScript := ``
+ result := []string{}
+ result = append(result, splits[0])
+ result = append(result, injectScript)
+ result = append(result, "")
+ result = append(result, splits[1])
+
+ db.Remove("index.html") // Remove the non-prefixed index.html
+ db.AddAsset("/index.html", []byte(strings.Join(result, "")))
+
+ // Add wails.js
+ wailsjsPath := fs.RelativePath("../../../internal/runtime/assets/server.js")
+ if rawData, err := ioutil.ReadFile(wailsjsPath); err == nil {
+ db.AddAsset("/wails.js", rawData)
+ }
+
+ targetFile := fs.RelativePath("../../../internal/webserver/webassetsdb.go")
+ s.addFileToDelete(targetFile)
+ f, err := os.Create(targetFile)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ f.WriteString(db.Serialize("db", "webserver"))
+
+ return nil
+}
+
+// BuildRuntime builds the javascript runtime used by the HTML client to connect to the websocket
+func (s *ServerBuilder) BuildRuntime(options *Options) error {
+
+ sourceDir := fs.RelativePath("../../../internal/runtime/js")
+ if err := s.NpmInstall(sourceDir); err != nil {
+ return err
+ }
+
+ options.Logger.Write(" - Embedding Runtime...")
+ envvars := []string{"WAILSPLATFORM=" + options.Platform}
+ var err error
+ if err = s.NpmRunWithEnvironment(sourceDir, "build:server", false, envvars); err != nil {
+ return err
+ }
+
+ options.Logger.Writeln("done.")
+ return nil
+}
diff --git a/v2/test/disable-resize/basic.go b/v2/test/disable-resize/basic.go
new file mode 100644
index 000000000..69de872be
--- /dev/null
+++ b/v2/test/disable-resize/basic.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "fmt"
+
+ wails "github.com/wailsapp/wails/v2"
+)
+
+// Basic application struct
+type Basic struct {
+ runtime *wails.Runtime
+}
+
+// newBasic creates a new Basic application struct
+func newBasic() *Basic {
+ return &Basic{}
+}
+
+// WailsInit is called at application startup
+func (b *Basic) WailsInit(runtime *wails.Runtime) error {
+ // Perform your setup here
+ b.runtime = runtime
+ runtime.Window.SetTitle("minmax")
+ return nil
+}
+
+// WailsShutdown is called at application termination
+func (b *Basic) WailsShutdown() {
+ // Perform your teardown here
+}
+
+// Greet returns a greeting for the given name
+func (b *Basic) Greet(name string) string {
+ return fmt.Sprintf("Hello %s!", name)
+}
diff --git a/v2/test/disable-resize/frontend/index.html b/v2/test/disable-resize/frontend/index.html
new file mode 100644
index 000000000..d39fbc6d6
--- /dev/null
+++ b/v2/test/disable-resize/frontend/index.html
@@ -0,0 +1,18 @@
+
+
+