Compare commits
20 Commits
19a2b3d443
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
370e056a2b | |
|
|
55e5136e88 | |
|
|
2586e60f0c | |
|
|
7a7e99337f | |
|
|
f52b9a9c53 | |
|
|
9350346b4c | |
|
|
728f2d67d0 | |
|
|
5f81ed1756 | |
|
|
7ba50976cf | |
|
|
e8e5b7e409 | |
|
|
cad2d85ce4 | |
|
|
013e6c5b01 | |
|
|
e4d7906cb5 | |
|
|
0fd974266c | |
|
|
521b3eef20 | |
|
|
ef4e0b4ac2 | |
|
|
15a2fe7420 | |
|
|
ef022499e7 | |
|
|
fd536075c5 | |
|
|
996bfea07c |
|
|
@ -1,2 +1,4 @@
|
||||||
*/.env
|
*/.env
|
||||||
test
|
test
|
||||||
|
*/.DS_STORE
|
||||||
|
.DS_STORE
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.tabSize": 2
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# watchtogether
|
||||||
|
|
||||||
|
This was a project that I created over 7 days of working while having covid :)
|
||||||
|
|
||||||
|
I probably won't do much with the codebase, but enjoy how I basically figured out how to sync video streams with multiple clients
|
||||||
|
|
||||||
|
## The Stack
|
||||||
|
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
- next js
|
||||||
|
- websockets
|
||||||
|
- chakra-ui
|
||||||
|
- react-player
|
||||||
|
|
||||||
|
- backend
|
||||||
|
|
||||||
|
- go
|
||||||
|
- gorilla/websockets
|
||||||
|
|
||||||
|
## how 2 deploy?
|
||||||
|
|
||||||
|
you don't
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
cmd/watchtogether/watchtogether
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="PROJECT_FILES" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="ALL" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="8a64704d-5500-41a6-aa4c-e275933fc58c" name="Changes" comment="">
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="Go File" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="GOROOT" url="file:///usr/lib/go" />
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
||||||
|
</component>
|
||||||
|
<component name="GitSEFilterConfiguration">
|
||||||
|
<file-type-list>
|
||||||
|
<filtered-out-file-type name="LOCAL_BRANCH" />
|
||||||
|
<filtered-out-file-type name="REMOTE_BRANCH" />
|
||||||
|
<filtered-out-file-type name="TAG" />
|
||||||
|
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
|
||||||
|
</file-type-list>
|
||||||
|
</component>
|
||||||
|
<component name="GoLibraries">
|
||||||
|
<option name="indexEntireGoPath" value="false" />
|
||||||
|
</component>
|
||||||
|
<component name="MarkdownSettingsMigration">
|
||||||
|
<option name="stateVersion" value="1" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectId" id="2456l5ltkI1NptLGqpacBxKAWdj" />
|
||||||
|
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
|
||||||
|
<ConfirmationsSetting value="2" id="Add" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent">
|
||||||
|
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
|
||||||
|
<property name="DefaultGoTemplateProperty" value="Go File" />
|
||||||
|
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||||
|
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||||
|
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||||
|
<property name="go.format.on.save.advertiser.fired" value="true" />
|
||||||
|
<property name="go.formatter.settings.were.checked" value="true" />
|
||||||
|
<property name="go.import.settings.migrated" value="true" />
|
||||||
|
<property name="go.modules.go.list.on.any.changes.was.set" value="true" />
|
||||||
|
<property name="go.sdk.automatically.set" value="true" />
|
||||||
|
<property name="go.watchers.conflict.with.on.save.actions.check.performed" value="true" />
|
||||||
|
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="RunManager">
|
||||||
|
<configuration name="go build github.com/qpixel/watchtogether/cmd/watchtogether" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
|
||||||
|
<module name="backend" />
|
||||||
|
<working_directory value="$PROJECT_DIR$" />
|
||||||
|
<kind value="PACKAGE" />
|
||||||
|
<package value="github.com/qpixel/watchtogether/cmd/watchtogether" />
|
||||||
|
<directory value="$PROJECT_DIR$" />
|
||||||
|
<filePath value="$PROJECT_DIR$/cmd/watchtogether/main.go" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<recent_temporary>
|
||||||
|
<list>
|
||||||
|
<item itemvalue="Go Build.go build github.com/qpixel/watchtogether/cmd/watchtogether" />
|
||||||
|
</list>
|
||||||
|
</recent_temporary>
|
||||||
|
</component>
|
||||||
|
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
<component name="Vcs.Log.Tabs.Properties">
|
||||||
|
<option name="TAB_STATES">
|
||||||
|
<map>
|
||||||
|
<entry key="MAIN">
|
||||||
|
<value>
|
||||||
|
<State />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="VcsManagerConfiguration">
|
||||||
|
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="VgoProject">
|
||||||
|
<integration-enabled>true</integration-enabled>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
localhost:3001
|
||||||
|
|
||||||
|
file_server
|
||||||
|
reverse_proxy 127.0.0.1:8080
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# backend
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/bash
|
||||||
|
VERSION=v1.0.0
|
||||||
|
COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
ENVIRONMENT=$ENV
|
||||||
|
BUILDSTRING="$VERSION-$COMMIT_HASH-$ENVIRONMENT"
|
||||||
|
echo The environment is "$ENVIRONMENT"
|
||||||
|
echo Building watch together backend "$VERSION-$COMMIT_HASH"
|
||||||
|
if [[ "$ENVIRONMENT" == "dev" ]]; then
|
||||||
|
go build -gcflags="all=-N -l" -ldflags "-X github.com/qpixel/watchtogether/cmd/watchtogether/main.VERSION=${BUILDSTRING} -X github.com/qpixel/watchtogether/cmd/watchtogether/main.ENVIRONMENT=${ENVIRONMENT}";
|
||||||
|
else
|
||||||
|
go build -ldflags "-X github.com/qpixel/watchtogether/cmd/watchtogether/main.VERSION=${BUILDSTRING} -X github.com/ubergeek77/uberbot/v2/core.ENVIRONMENT=${ENVIRONMENT}";
|
||||||
|
fi
|
||||||
|
|
@ -1,72 +1,62 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"encoding/json"
|
||||||
"fmt"
|
"github.com/gorilla/mux"
|
||||||
"github.com/peterbourgon/ff/v3"
|
"github.com/qpixel/watchtogether/internal/ws"
|
||||||
"github.com/qpixel/watchtogether/internal/logger"
|
tlog "github.com/ubergeek77/tinylog"
|
||||||
"github.com/qpixel/watchtogether/internal/server"
|
"net/http"
|
||||||
"github.com/ubergeek77/tinylog"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type flags struct {
|
var log = tlog.NewTaggedLogger("Logger", tlog.NewColor("38;5;111"))
|
||||||
loglvl int
|
|
||||||
port int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFlags(args []string) (flgs flags, err error) {
|
|
||||||
fs := flag.NewFlagSet(args[0], flag.ContinueOnError)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
loglvl = fs.Int("loglvl", tinylog.TraceLevel, "sets the log level (also via LOG_LEVEL)")
|
VERSION string
|
||||||
port = fs.Int("port", 4000, "sets the port to use (also via PORT)")
|
ENVIRONMENT string
|
||||||
)
|
)
|
||||||
// Parse the command line flags from above
|
|
||||||
err = ff.Parse(fs, args[1:], ff.WithEnvVarNoPrefix())
|
|
||||||
if err != nil {
|
|
||||||
return flgs, err
|
|
||||||
}
|
|
||||||
return flags{
|
|
||||||
loglvl: *loglvl,
|
|
||||||
port: *port,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := run(os.Args); err != nil {
|
r := mux.NewRouter()
|
||||||
tinylog.DefaultLogger().Errorf("error from main.run(): %s\n", err)
|
hub := ws.NewHub()
|
||||||
os.Exit(1)
|
go hub.Run()
|
||||||
}
|
|
||||||
|
|
||||||
}
|
r.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ws.ServeWs(hub, w, r)
|
||||||
func run(args []string) error {
|
})
|
||||||
|
r.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
|
||||||
flgs, err := newFlags(args)
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
resp := make(map[string]interface{})
|
||||||
|
resp["status"] = "ok"
|
||||||
|
resp["users"] = len(hub.Clients)
|
||||||
|
jsonResp, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Fatalf("error in marshaling json. err: %s", err.Error())
|
||||||
}
|
}
|
||||||
// setup logger with defaults
|
_, err = w.Write(jsonResp)
|
||||||
lgr := logger.NewLogger(flgs.loglvl, "WatchTogether")
|
|
||||||
|
|
||||||
lgr.Infof("go runtime ver: %s", runtime.Version())
|
|
||||||
lgr.Infof("Logging level has been set to %d", lgr.LogLevel)
|
|
||||||
|
|
||||||
mr := server.NewMuxRouter()
|
|
||||||
|
|
||||||
serverDriver := server.NewDriver()
|
|
||||||
|
|
||||||
params := server.NewServerParams(lgr, serverDriver)
|
|
||||||
|
|
||||||
s, err := server.NewServer(mr, params)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lgr.Errorf("err: %s", err)
|
return
|
||||||
lgr.Fatal("error in server.NewServer")
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
r.Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
resp := make(map[string]interface{})
|
||||||
|
resp["status"] = "ok"
|
||||||
|
jsonResp, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error in marshaling json. err: %s", err.Error())
|
||||||
|
}
|
||||||
|
_, err = w.Write(jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
log.Infof("starting backend")
|
||||||
|
log.Infof("running version %s. environment: %s", VERSION, ENVIRONMENT)
|
||||||
|
err := http.ListenAndServe(":8080", r)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%s", err.Error())
|
||||||
|
log.Fatalf("unable to serve on port 8080")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Addr = fmt.Sprintf(":%d", flgs.port)
|
|
||||||
|
|
||||||
return s.ListenAndServe()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
|
@ -2,16 +2,8 @@ module github.com/qpixel/watchtogether
|
||||||
|
|
||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
replace (
|
|
||||||
github.com/qpixel/tloghttp => ../../tloghttp
|
|
||||||
github.com/qpixel/tlogbuilder => ../../tlogbuilder
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/justinas/alice v1.2.0
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/peterbourgon/ff/v3 v3.1.2
|
github.com/ubergeek77/tinylog v1.0.0 // indirect
|
||||||
github.com/qpixel/tloghttp v0.0.0-20211222065322-cd8d1a945a36
|
|
||||||
github.com/qpixel/tlogbuilder v0.0.0
|
|
||||||
github.com/ubergeek77/tinylog v1.0.0
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,6 @@
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
|
||||||
github.com/peterbourgon/ff/v3 v3.1.2 h1:0GNhbRhO9yHA4CC27ymskOsuRpmX0YQxwxM9UPiP6JM=
|
|
||||||
github.com/peterbourgon/ff/v3 v3.1.2/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
|
|
||||||
github.com/ubergeek77/tinylog v1.0.0 h1:gsq98mbig3LDWhsizOe2tid12wHUz/mrkDlmgJ0MZG4=
|
github.com/ubergeek77/tinylog v1.0.0 h1:gsq98mbig3LDWhsizOe2tid12wHUz/mrkDlmgJ0MZG4=
|
||||||
github.com/ubergeek77/tinylog v1.0.0/go.mod h1:NzUi4PkRG2hACL4cGgmW7db6EaKjAeqrqlVQnJdw78Q=
|
github.com/ubergeek77/tinylog v1.0.0/go.mod h1:NzUi4PkRG2hACL4cGgmW7db6EaKjAeqrqlVQnJdw78Q=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright 2018 The Go Cloud Development Kit Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// @gilcrest - removed TLSServer interface
|
|
||||||
|
|
||||||
// Package driver defines an interface for custom HTTP listeners.
|
|
||||||
// Application code should use package server.
|
|
||||||
|
|
||||||
package driver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server dispatches requests to an http.Handler.
|
|
||||||
type Server interface {
|
|
||||||
// ListenAndServe listens on the TCP network address addr and then
|
|
||||||
// calls Serve with handler to handle requests on incoming connections.
|
|
||||||
// The addr argument will be a non-empty string specifying "host:port".
|
|
||||||
// The http.Handler will always be non-nil.
|
|
||||||
// Drivers must block until serving is done (or
|
|
||||||
// return an error if serving can't occur for some reason), serve
|
|
||||||
// requests to the given http.Handler, and be interruptable by Shutdown.
|
|
||||||
// Drivers should use the given address if they serve using TCP directly.
|
|
||||||
ListenAndServe(addr string, h http.Handler) error
|
|
||||||
|
|
||||||
// Shutdown gracefully shuts down the server without interrupting
|
|
||||||
// any active connections. If the provided context expires before
|
|
||||||
// the shutdown is complete, Shutdown returns the context's error,
|
|
||||||
// otherwise it returns any error returned from closing the Server's
|
|
||||||
// underlying Listener(s).
|
|
||||||
Shutdown(ctx context.Context) error
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ubergeek77/tinylog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewLogger(lvl int, tag string) *tinylog.Logger {
|
|
||||||
// cfg contains our custom config for tinylog
|
|
||||||
cfg := tinylog.NewConfig()
|
|
||||||
cfg.LogLevel = lvl
|
|
||||||
cfg.LogPrefix = tinylog.GenerateTag(tag, tinylog.NewColor("38;5;133"), cfg)
|
|
||||||
|
|
||||||
//initialize the instance of tinylog
|
|
||||||
lgr := tinylog.NewLogger(cfg)
|
|
||||||
|
|
||||||
return lgr
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/justinas/alice"
|
|
||||||
"github.com/qpixel/tlogbuilder"
|
|
||||||
"github.com/qpixel/tloghttp"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MIT License
|
|
||||||
//
|
|
||||||
//Copyright (c) 2017 Dan Gillis
|
|
||||||
//
|
|
||||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
//of this software and associated documentation files (the "Software"), to deal
|
|
||||||
//in the Software without restriction, including without limitation the rights
|
|
||||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
//copies of the Software, and to permit persons to whom the Software is
|
|
||||||
//furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
//The above copyright notice and this permission notice shall be included in all
|
|
||||||
//copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
//SOFTWARE.
|
|
||||||
|
|
||||||
// LoggerChain returns a middleware chain (via alice.Chain)
|
|
||||||
// initialized with all the standard middleware handlers for logging. The logger
|
|
||||||
// will be added to the request context for subsequent use with pre-populated
|
|
||||||
// fields, including the request method, url, status, size, duration, remote IP,
|
|
||||||
// user agent, referer. A unique Request ID is also added to the logger, context
|
|
||||||
// and response headers.
|
|
||||||
func (s *Server) loggerChain() alice.Chain {
|
|
||||||
ac := alice.New(tloghttp.NewHandler(s.logger),
|
|
||||||
tloghttp.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
|
|
||||||
tlogbuilder.CreateBuilder(tloghttp.FromRequest(r)).
|
|
||||||
Info().
|
|
||||||
Str("method", r.Method).
|
|
||||||
Stringer("url", r.URL).
|
|
||||||
Int("status", status).
|
|
||||||
Int("size", size).
|
|
||||||
Dur("duration", duration).
|
|
||||||
Msg("request logged").
|
|
||||||
Build()
|
|
||||||
}),
|
|
||||||
tloghttp.RemoteAddrHandler("remote_ip"),
|
|
||||||
tloghttp.UserAgentHandler("user_agent"),
|
|
||||||
tloghttp.RefererHandler("referer"),
|
|
||||||
//tloghttp.RequestIDHandler("request_id", "Request-Id"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return ac
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
const (
|
|
||||||
contentTypeHeaderKey string = "Content-Type"
|
|
||||||
appJSONContentTypeHeaderVal string = "application/json",
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) routes() {
|
|
||||||
s.router.Handle("/", func(http.ResponseWriter, *http.Response) {
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
// Copyright 2018 The Go Cloud Development Kit Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
// @QPixel edits - I have made a copy of the go-api-basic server code and made
|
|
||||||
// the following changes:
|
|
||||||
//
|
|
||||||
// - removed zerolog dependency
|
|
||||||
|
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/qpixel/watchtogether/internal/driver"
|
|
||||||
"github.com/ubergeek77/tinylog"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const pathPrefix = "/api"
|
|
||||||
|
|
||||||
// Server represents an HTTP server
|
|
||||||
type Server struct {
|
|
||||||
router *mux.Router
|
|
||||||
driver driver.Server
|
|
||||||
|
|
||||||
logger *tinylog.Logger
|
|
||||||
|
|
||||||
Addr string
|
|
||||||
|
|
||||||
// Authorization
|
|
||||||
|
|
||||||
// Services
|
|
||||||
}
|
|
||||||
|
|
||||||
//type LoggerService interface {
|
|
||||||
// Read() logger.LoggerResponse
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
|
|
||||||
// SvrParams is the set of configuration parameters for a Server
|
|
||||||
type SvrParams struct {
|
|
||||||
// Logger is used for the server logging
|
|
||||||
Logger *tinylog.Logger
|
|
||||||
|
|
||||||
// Driver serves HTTP requests
|
|
||||||
Driver driver.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServerParams is an initializer for ServerParams
|
|
||||||
func NewServerParams(lgr *tinylog.Logger, d driver.Server) *SvrParams {
|
|
||||||
options := &SvrParams{
|
|
||||||
Logger: lgr,
|
|
||||||
Driver: d,
|
|
||||||
}
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer initializes a new Server and registers
|
|
||||||
// routes to the router mux
|
|
||||||
// todo add error checking
|
|
||||||
func NewServer(r *mux.Router, params *SvrParams) (*Server, error) {
|
|
||||||
s := &Server{router: r}
|
|
||||||
s.logger = params.Logger
|
|
||||||
s.driver = params.Driver
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ListenAndServe() error {
|
|
||||||
if s.Addr == "" {
|
|
||||||
return errors.New("server Addr is empty")
|
|
||||||
}
|
|
||||||
if s.router == nil {
|
|
||||||
return errors.New("server router is nil")
|
|
||||||
}
|
|
||||||
if s.driver == nil {
|
|
||||||
return errors.New("server driver is nil")
|
|
||||||
}
|
|
||||||
s.logger.Infof("server is listening on %s", s.Addr)
|
|
||||||
return s.driver.ListenAndServe(s.Addr, s.router)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown will gracefully shut down the server without interrupting any active connections
|
|
||||||
func (s *Server) Shutdown(ctx context.Context) error {
|
|
||||||
if s.driver == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return s.driver.Shutdown(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Driver implements the driver.Server interface. The zero value is a valid http.Server
|
|
||||||
type Driver struct {
|
|
||||||
Server http.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDriver intializes a Driver with http.Server using default timeouts
|
|
||||||
func NewDriver() *Driver {
|
|
||||||
return &Driver{
|
|
||||||
Server: http.Server{
|
|
||||||
ReadTimeout: 30 * time.Second,
|
|
||||||
WriteTimeout: 30 * time.Second,
|
|
||||||
IdleTimeout: 120 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenAndServe sets the address and handler on Driver's http.Server
|
|
||||||
func (d *Driver) ListenAndServe(addr string, h http.Handler) error {
|
|
||||||
d.Server.Addr = addr
|
|
||||||
d.Server.Handler = h
|
|
||||||
return d.Server.ListenAndServe()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown gracefully shuts down the server without interrupting any active connections,
|
|
||||||
// by calling Shutdown on Driver's http.Server
|
|
||||||
func (d *Driver) Shutdown(ctx context.Context) error {
|
|
||||||
return d.Server.Shutdown(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMuxRouter initializes a gorilla/mux router and
|
|
||||||
// adds the /api subroute to it
|
|
||||||
func NewMuxRouter() *mux.Router {
|
|
||||||
// initializer gorilla/mux router
|
|
||||||
r := mux.NewRouter()
|
|
||||||
|
|
||||||
// send Router through PathPrefix method to validate any standard
|
|
||||||
// subroutes you may want for your APIs. e.g. I always want to be
|
|
||||||
// sure that every request has "/api" as part of its path prefix
|
|
||||||
// without having to put it into every handle path in my various
|
|
||||||
// routing functions
|
|
||||||
s := r.PathPrefix(pathPrefix).Subrouter()
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func decoderErr(err error) error {
|
|
||||||
switch {
|
|
||||||
case err == io.EOF:
|
|
||||||
return errors.New("request body cannot be empty")
|
|
||||||
case err == io.ErrUnexpectedEOF:
|
|
||||||
return errors.New("malformed json")
|
|
||||||
case err != nil:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
package ws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// time allowed to write a message to the peer.
|
||||||
|
writeWait = 10 * time.Second
|
||||||
|
|
||||||
|
pongWait = 60 * time.Second
|
||||||
|
|
||||||
|
pingPeriod = (pongWait * 9) / 10
|
||||||
|
|
||||||
|
maxMessageSize = 512
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
newline = []byte{'\n'}
|
||||||
|
space = []byte{' '}
|
||||||
|
)
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
hub *Hub
|
||||||
|
conn *websocket.Conn
|
||||||
|
send chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// readPump pumps messages from the websocket connection to the hub.
|
||||||
|
//
|
||||||
|
// The application runs readPump in a per-connection goroutine. The application
|
||||||
|
// ensures that there is at most one reader on a connection by executing all
|
||||||
|
// reads from this goroutine.
|
||||||
|
func (c *Client) readPump() {
|
||||||
|
defer func() {
|
||||||
|
c.hub.unregister <- c
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
c.conn.SetReadLimit(maxMessageSize)
|
||||||
|
c.conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||||
|
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||||
|
for {
|
||||||
|
_, message, err := c.conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
||||||
|
log.Infof("error: %v", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
|
||||||
|
c.hub.broadcast <- RawMessage{
|
||||||
|
Client: c,
|
||||||
|
Data: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePump pumps messages from the hub to the websocket connection.
|
||||||
|
//
|
||||||
|
// A goroutine running writePump is started for each connection. The
|
||||||
|
// application ensures that there is at most one writer to a connection by
|
||||||
|
// executing all writes from this goroutine.
|
||||||
|
func (c *Client) writePump() {
|
||||||
|
ticker := time.NewTicker(pingPeriod)
|
||||||
|
defer func() {
|
||||||
|
ticker.Stop()
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message, ok := <-c.send:
|
||||||
|
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if !ok {
|
||||||
|
// The hub closed the channel.
|
||||||
|
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := c.conn.NextWriter(websocket.TextMessage)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(message)
|
||||||
|
|
||||||
|
// Add queued chat messages to the current websocket message.
|
||||||
|
n := len(c.send)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
w.Write(newline)
|
||||||
|
w.Write(<-c.send)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeWs handles websocket requests from the peer.
|
||||||
|
func ServeWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
||||||
|
upgrader.CheckOrigin = func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
|
||||||
|
client.hub.register <- client
|
||||||
|
|
||||||
|
// Allow collection of memory referenced by the caller by doing all work in
|
||||||
|
// new goroutines.
|
||||||
|
go client.writePump()
|
||||||
|
go client.readPump()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package ws
|
||||||
|
|
||||||
|
//todo better data deserialization
|
||||||
|
|
||||||
|
type IdentityData struct {
|
||||||
|
ClientID string `json:"clientID"`
|
||||||
|
User User `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleIdentifyEvent(message *Message) {
|
||||||
|
d := message.Data.(map[string]interface{})
|
||||||
|
if id, ok := d["clientID"]; ok {
|
||||||
|
log.Infof("Client %s has sent identify event", id.(string))
|
||||||
|
}
|
||||||
|
user := d["user"].(map[string]interface{})
|
||||||
|
userId := user["id"].(string)
|
||||||
|
playhead := message.hub.State.playhead
|
||||||
|
paused := message.hub.State.paused
|
||||||
|
m := Message{
|
||||||
|
MessageData: MessageData{
|
||||||
|
Type: Identify,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"admin": message.hub.State.IsAdmin(userId),
|
||||||
|
"playlist": "http://localhost:8081/BelleOpening.m3u8",
|
||||||
|
"hasController": message.hub.State.IsController(userId),
|
||||||
|
"playhead": playhead,
|
||||||
|
"paused": paused,
|
||||||
|
"user": d["user"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
message.send <- m.SerializeMessage().Data
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePingEvent(message *Message) {
|
||||||
|
m := Message{
|
||||||
|
message.Client,
|
||||||
|
MessageData{
|
||||||
|
Type: Pong,
|
||||||
|
Data: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
message.send <- m.SerializeMessage().Data
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetPlayhead(message *Message) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSetPlayhead(message *Message) {
|
||||||
|
d := message.Data.(map[string]interface{})
|
||||||
|
m := Message{
|
||||||
|
MessageData: MessageData{
|
||||||
|
Type: SetPlayhead,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"playhead": d["playhead"],
|
||||||
|
"paused": d["paused"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
log.Infof("Received SetPlayhead event. playhead is at %s", d["playhead"])
|
||||||
|
err := message.hub.State.setPlayhead(d["playhead"].(float64))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to set playhead. %s", err)
|
||||||
|
}
|
||||||
|
err = message.hub.State.setPaused(d["paused"].(bool))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to set paused. %s", err)
|
||||||
|
}
|
||||||
|
for client := range message.Client.hub.Clients {
|
||||||
|
if client == message.Client {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
client.send <- m.SerializeMessage().Data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package ws
|
||||||
|
|
||||||
|
type Hub struct {
|
||||||
|
// Registered Clients
|
||||||
|
Clients map[*Client]bool
|
||||||
|
|
||||||
|
// State
|
||||||
|
State *State
|
||||||
|
|
||||||
|
// Inbound messages from the Clients
|
||||||
|
broadcast chan RawMessage
|
||||||
|
|
||||||
|
// Register requests from the Clients
|
||||||
|
register chan *Client
|
||||||
|
|
||||||
|
// Unregister requests from Clients
|
||||||
|
unregister chan *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHub() *Hub {
|
||||||
|
return &Hub{
|
||||||
|
broadcast: make(chan RawMessage),
|
||||||
|
register: make(chan *Client),
|
||||||
|
unregister: make(chan *Client),
|
||||||
|
Clients: make(map[*Client]bool),
|
||||||
|
State: NewState(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hub) handleMessage(rm RawMessage) {
|
||||||
|
m := rm.UnserializeData()
|
||||||
|
switch m.Type {
|
||||||
|
case Identify:
|
||||||
|
handleIdentifyEvent(&m)
|
||||||
|
case Ping:
|
||||||
|
handlePingEvent(&m)
|
||||||
|
case GetPlayhead:
|
||||||
|
handleGetPlayhead(&m)
|
||||||
|
case SetPlayhead:
|
||||||
|
handleSetPlayhead(&m)
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hub) Run() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case client := <-h.register:
|
||||||
|
h.Clients[client] = true
|
||||||
|
case client := <-h.unregister:
|
||||||
|
if _, ok := h.Clients[client]; ok {
|
||||||
|
delete(h.Clients, client)
|
||||||
|
close(client.send)
|
||||||
|
}
|
||||||
|
case message := <-h.broadcast:
|
||||||
|
go h.handleMessage(message)
|
||||||
|
//for client := range h.Clients {
|
||||||
|
// select {
|
||||||
|
// case client.send <- message:
|
||||||
|
// default:
|
||||||
|
// close(client.send)
|
||||||
|
// delete(h.Clients, client)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package ws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
tlog "github.com/ubergeek77/tinylog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = tlog.NewTaggedLogger("WS", tlog.NewColor("38;5;111"))
|
||||||
|
|
||||||
|
type MessageTypes float64
|
||||||
|
|
||||||
|
// todo rewrite to use an event handler system
|
||||||
|
const (
|
||||||
|
Ping MessageTypes = iota
|
||||||
|
Pong
|
||||||
|
Identify
|
||||||
|
GetPlayhead
|
||||||
|
SetPlayhead
|
||||||
|
)
|
||||||
|
|
||||||
|
type MessageData struct {
|
||||||
|
Type MessageTypes `json:"type"`
|
||||||
|
RawData json.RawMessage `json:"data,omitempty"`
|
||||||
|
Data interface{} `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
*Client
|
||||||
|
MessageData
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawMessage struct {
|
||||||
|
Client *Client
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm RawMessage) UnserializeData() Message {
|
||||||
|
var md MessageData
|
||||||
|
if err := json.Unmarshal(rm.Data, &md); err != nil {
|
||||||
|
log.Errorf("error unmarshalling message, %s", err.Error())
|
||||||
|
}
|
||||||
|
if md.RawData != nil && len(md.RawData) > 0 {
|
||||||
|
if err := json.Unmarshal(md.RawData, &md.Data); err != nil {
|
||||||
|
// handle error
|
||||||
|
log.Errorf("error unmarshalling data, %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := Message{
|
||||||
|
Client: rm.Client,
|
||||||
|
MessageData: md,
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Message) SerializeMessage() RawMessage {
|
||||||
|
var err error
|
||||||
|
if m.Data != nil {
|
||||||
|
m.RawData, err = json.Marshal(m.Data)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to marshal data, %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(m.MessageData)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to marshal message, %s", err.Error())
|
||||||
|
}
|
||||||
|
return RawMessage{
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package ws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
sync.RWMutex
|
||||||
|
|
||||||
|
playhead float64
|
||||||
|
controllerUserId string
|
||||||
|
adminUserId string
|
||||||
|
paused bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewState() *State {
|
||||||
|
return &State{
|
||||||
|
playhead: 0,
|
||||||
|
controllerUserId: "218072060923084802",
|
||||||
|
adminUserId: "218072060923084802",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) setPlayhead(playhead float64) error {
|
||||||
|
if s == nil {
|
||||||
|
return errors.New("unable to find state")
|
||||||
|
}
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
s.playhead = playhead
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (s *State) setPaused(paused bool) error {
|
||||||
|
if s == nil {
|
||||||
|
return errors.New("unable to find state")
|
||||||
|
}
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
s.paused = paused
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (s *State) IsController(userID string) bool {
|
||||||
|
if userID == s.controllerUserId {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (s *State) IsAdmin(userID string) bool {
|
||||||
|
if userID == s.adminUserId {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package ws
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
package services
|
|
||||||
|
|
||||||
import "github.com/ubergeek77/tinylog"
|
|
||||||
|
|
||||||
// LoggerService reads and updates the logger state
|
|
||||||
type LoggerService struct {
|
|
||||||
Logger tinylog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLoggerService creates a new instance of the logging service
|
|
||||||
func NewLoggerService(logger tinylog.Logger) *LoggerService {
|
|
||||||
return &LoggerService{Logger: logger}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
DISCORD_ID=
|
||||||
|
DISCORD_SECRET=
|
||||||
|
SECRET=
|
||||||
|
NEXT_PUBLIC_CLIENT_ID=
|
||||||
|
NEXTAUTH_URL=
|
||||||
|
NEXT_PUBLIC_WS_URI=
|
||||||
|
|
@ -32,3 +32,5 @@ yarn-error.log*
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
|
≈
|
||||||
|
|
@ -1,39 +1 @@
|
||||||
# Example app with [chakra-ui](https://github.com/chakra-ui/chakra-ui) and TypeScript
|
# frontend
|
||||||
|
|
||||||
This example features how to use [chakra-ui](https://github.com/chakra-ui/chakra-ui) as the component library within a Next.js app with TypeScript.
|
|
||||||
|
|
||||||
Next.js and chakra-ui have built-in TypeScript declarations, so we'll get autocompletion for their modules straight away.
|
|
||||||
|
|
||||||
We are connecting the Next.js `_app.js` with `chakra-ui`'s Provider and theme so the pages can have app-wide dark/light mode. We are also creating some components which shows the usage of `chakra-ui`'s style props.
|
|
||||||
|
|
||||||
## Preview
|
|
||||||
|
|
||||||
Preview the example live on [StackBlitz](http://stackblitz.com/):
|
|
||||||
|
|
||||||
[](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-chakra-ui-typescript)
|
|
||||||
|
|
||||||
## Deploy your own
|
|
||||||
|
|
||||||
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
|
|
||||||
|
|
||||||
[](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-chakra-ui-typescript&project-name=with-chakra-ui-typescript&repository-name=with-chakra-ui-typescript)
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
|
|
||||||
### Using `create-next-app`
|
|
||||||
|
|
||||||
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx create-next-app --example with-chakra-ui-typescript with-chakra-ui-typescript-app
|
|
||||||
# or
|
|
||||||
yarn create next-app --example with-chakra-ui-typescript with-chakra-ui-typescript-app
|
|
||||||
```
|
|
||||||
|
|
||||||
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
Chakra has supported Gradients and RTL in `v1.1`. To utilize RTL, [add RTL direction and swap](https://chakra-ui.com/docs/features/rtl-support).
|
|
||||||
|
|
||||||
If you don't have multi-direction app, you should make `<Html lang="ar" dir="rtl">` inside `_document.ts`.
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||||
|
enabled: process.env.ANALYZE === 'true'
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = withBundleAnalyzer({})
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"name": "frontend",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next",
|
"dev": "next",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start"
|
"start": "next start",
|
||||||
|
"analyze": "cross-env ANALYZE=true next build",
|
||||||
|
"analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
|
||||||
|
"analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/icons": "^1.0.5",
|
"@chakra-ui/icons": "^1.0.5",
|
||||||
|
|
@ -11,19 +15,30 @@
|
||||||
"@chakra-ui/theme-tools": "1.1.2",
|
"@chakra-ui/theme-tools": "1.1.2",
|
||||||
"@emotion/react": "11.1.5",
|
"@emotion/react": "11.1.5",
|
||||||
"@emotion/styled": "11.1.5",
|
"@emotion/styled": "11.1.5",
|
||||||
|
"consola": "^2.15.3",
|
||||||
|
"events": "^3.3.0",
|
||||||
"framer-motion": "^4.0.3",
|
"framer-motion": "^4.0.3",
|
||||||
"next": "latest",
|
"next": "latest",
|
||||||
"next-auth": "^4.1.2",
|
"next-auth": "^4.1.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-player": "^2.9.0",
|
"react-player": "^2.9.0",
|
||||||
"websocket": "^1.0.34"
|
"uuid": "^8.3.2",
|
||||||
|
"ws": "^8.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@next/bundle-analyzer": "^12.0.8",
|
||||||
|
"@types/events": "^3.0.0",
|
||||||
"@types/node": "^14.6.0",
|
"@types/node": "^14.6.0",
|
||||||
"@types/react": "^17.0.3",
|
"@types/react": "^17.0.3",
|
||||||
"@types/react-dom": "^17.0.3",
|
"@types/react-dom": "^17.0.3",
|
||||||
|
"@types/uuid": "^8.3.4",
|
||||||
"@types/websocket": "^1.0.4",
|
"@types/websocket": "^1.0.4",
|
||||||
|
"@types/ws": "^8.2.2",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"typescript": "4.3.2"
|
"typescript": "4.3.2"
|
||||||
|
},
|
||||||
|
"volta": {
|
||||||
|
"node": "16.13.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
import { Flex, useColorMode, FlexProps } from '@chakra-ui/react'
|
import React, { FC } from "react";
|
||||||
|
import { Flex, useColorMode, FlexProps } from "@chakra-ui/react";
|
||||||
|
|
||||||
export const Container = (props: FlexProps) => {
|
export const Container: FC<FlexProps> = (props: FlexProps) => {
|
||||||
const { colorMode } = useColorMode()
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
const bgColor = { light: 'gray.50', dark: 'gray.900' }
|
const bgColor = { light: "gray.50", dark: "gray.900" };
|
||||||
|
|
||||||
const color = { light: 'black', dark: 'white' }
|
const color = { light: "black", dark: "white" };
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
alignItems="center"
|
|
||||||
justifyContent="flex-start"
|
justifyContent="flex-start"
|
||||||
bg={bgColor[colorMode]}
|
bg={bgColor[colorMode]}
|
||||||
color={color[colorMode]}
|
color={color[colorMode]}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Container.defaultProps = {
|
||||||
|
alignItems: "center",
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Box, Flex, Heading } from "@chakra-ui/layout";
|
||||||
|
import { Text } from "@chakra-ui/react";
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
|
const Header: FC = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
as="header"
|
||||||
|
position="fixed"
|
||||||
|
w="100%"
|
||||||
|
justifyContent="space-between"
|
||||||
|
outline="1"
|
||||||
|
padding="1"
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
alignSelf="flex-start"
|
||||||
|
bgGradient="linear(to-l, #7928CA, #FF0080)"
|
||||||
|
bgClip="text"
|
||||||
|
height="100%"
|
||||||
|
>
|
||||||
|
<Heading fontSize="2rem">Watch Together</Heading>
|
||||||
|
<Text
|
||||||
|
fontStyle="italic"
|
||||||
|
ml="2"
|
||||||
|
mt="1"
|
||||||
|
fontSize="2xl"
|
||||||
|
fontWeight="semibold"
|
||||||
|
>
|
||||||
|
admin
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
|
|
@ -1,12 +1,23 @@
|
||||||
import { Flex, Heading } from "@chakra-ui/react";
|
import { Flex, Heading, keyframes } from "@chakra-ui/react";
|
||||||
|
import { css } from "@emotion/react";
|
||||||
|
|
||||||
|
const gradient = keyframes`
|
||||||
|
0% { background-position: 0% 0%; }
|
||||||
|
100% { background-position: 100% 0%; }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const Hero = ({ title }: { title: string }) => (
|
export const Hero = ({ title }: { title: string }) => (
|
||||||
<Flex
|
<Flex
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
height="100vh"
|
height="100vh"
|
||||||
bgGradient="linear(to-l, #7928CA, #FF0080)"
|
backgroundImage="linear-gradient(90deg,#cf5c5c,#c19b4a,#def2a0,#c6ee4a,#42eca6,#64b3d9,#208ea2,#498ada,#5b73df,#897ed3,#cf5c5c,#c19b4a)"
|
||||||
|
backgroundSize="1100% 100%"
|
||||||
bgClip="text"
|
bgClip="text"
|
||||||
|
css={css`
|
||||||
|
animation: ${gradient} 14s linear infinite;
|
||||||
|
`}
|
||||||
>
|
>
|
||||||
<Heading fontSize="6vw">{title}</Heading>
|
<Heading fontSize="6vw">{title}</Heading>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,83 @@
|
||||||
import React, { FC } from "react";
|
import { Box } from "@chakra-ui/react";
|
||||||
import ReactPlayer, { ReactPlayerProps } from "react-player";
|
import React, { forwardRef } from "react";
|
||||||
|
import ReactPlayer, { Config, ReactPlayerProps } from "react-player";
|
||||||
|
|
||||||
type PlayerProps = { id: string } & ReactPlayerProps;
|
const Player = forwardRef<ReactPlayer, ReactPlayerProps>((props, ref) => {
|
||||||
|
const config: Config = {
|
||||||
const Player: FC<PlayerProps> = ({ id, config }) => {
|
|
||||||
return <ReactPlayer url={id} config={config} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
Player.defaultProps = {
|
|
||||||
id: "",
|
|
||||||
config: {
|
|
||||||
file: {
|
file: {
|
||||||
forceHLS: true,
|
forceHLS: true,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (playerRef.current && typeof props.identity !== "undefined") {
|
||||||
|
// console.log(props.identity.playhead);
|
||||||
|
// playerRef.current.seekTo(props.identity.playhead);
|
||||||
|
// setPaused(props.identity.paused);
|
||||||
|
// }
|
||||||
|
// }, []);
|
||||||
|
// socket.emitter.once(SocketEvents.SetPlayhead, (e) => {
|
||||||
|
// console.log(e);
|
||||||
|
// playerRef.current.seekTo(e.playhead);
|
||||||
|
// setPaused(e.paused);
|
||||||
|
// });
|
||||||
|
// const onSeek = (playedSeconds: number) => {
|
||||||
|
// if (!props.identity.admin) return;
|
||||||
|
// if (paused) {
|
||||||
|
// socket?.send(
|
||||||
|
// MessageUtil.encode(
|
||||||
|
// new Message(MessageTypes.SetPlayhead, {
|
||||||
|
// playhead: playedSeconds,
|
||||||
|
// paused: true,
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// socket?.send(
|
||||||
|
// MessageUtil.encode(
|
||||||
|
// new Message(MessageTypes.SetPlayhead, {
|
||||||
|
// playhead: playedSeconds,
|
||||||
|
// paused: false,
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// const onPause = () => {
|
||||||
|
// if (!props.identity.admin) return;
|
||||||
|
// setPaused(true);
|
||||||
|
// socket?.send(
|
||||||
|
// MessageUtil.encode(
|
||||||
|
// new Message(MessageTypes.SetPlayhead, {
|
||||||
|
// playhead: playerRef.current.getCurrentTime(),
|
||||||
|
// paused: true,
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// const onPlay = () => {
|
||||||
|
// if (!props.identity.admin) return;
|
||||||
|
// setPaused(false);
|
||||||
|
// socket?.send(
|
||||||
|
// MessageUtil.encode(
|
||||||
|
// new Message(MessageTypes.SetPlayhead, {
|
||||||
|
// playhead: playerRef.current.getCurrentTime(),
|
||||||
|
// paused: false,
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
return (
|
||||||
|
<Box height="100vh" width="100vw">
|
||||||
|
<ReactPlayer
|
||||||
|
url={props.url}
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
config={config}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Player;
|
export default Player;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Avatar } from "@chakra-ui/avatar";
|
||||||
|
import { Box, Flex, Text } from "@chakra-ui/layout";
|
||||||
|
import { Session } from "next-auth";
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
|
interface UserDataProps {
|
||||||
|
user: Session["user"];
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserData: FC<UserDataProps> = ({ user: { name, image } }) => {
|
||||||
|
return (
|
||||||
|
<Flex alignSelf="flex-end" mr="2" mt="1">
|
||||||
|
<Avatar src={image} />
|
||||||
|
<Box ml="3" mt="2">
|
||||||
|
<Text fontWeight="bold">{name}</Text>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserData;
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { User } from "next-auth";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import PlayerSocket from "../ws/websocket";
|
||||||
|
|
||||||
|
interface useWSProps {
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo write websocket reconnector
|
||||||
|
const useWS = ({ user }: useWSProps): PlayerSocket | null => {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// todo checkout usecallback
|
||||||
|
const [socket, setSocket] = useState<PlayerSocket>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (socket !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let internalSocket = new PlayerSocket(user);
|
||||||
|
setSocket(internalSocket);
|
||||||
|
return () => {
|
||||||
|
if (internalSocket.readyState !== WebSocket.OPEN) {
|
||||||
|
return internalSocket.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useWS;
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
export enum MessageTypes {
|
||||||
|
Ping,
|
||||||
|
Pong,
|
||||||
|
Identify,
|
||||||
|
GetPlayhead,
|
||||||
|
SetPlayhead,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMessage {
|
||||||
|
type: MessageTypes;
|
||||||
|
data?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IMessage;
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
interface IUser {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IUser;
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import IUser from "./IUser";
|
||||||
|
|
||||||
|
interface IdentityData {
|
||||||
|
admin?: boolean;
|
||||||
|
hasController?: boolean;
|
||||||
|
clientID?: string;
|
||||||
|
playlist?: string;
|
||||||
|
playhead?: number;
|
||||||
|
paused?: boolean;
|
||||||
|
user: IUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IdentityData;
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export default interface SetPlayheadEvent {
|
||||||
|
playhead: number;
|
||||||
|
paused: boolean;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
enum SocketEvents {
|
||||||
|
Identify = "Identify",
|
||||||
|
SetPlayhead = "SetPlayhead",
|
||||||
|
GetPlayhead = "GetPlayhead",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SocketEvents;
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import NextDocument, { Html, Head, Main, NextScript } from 'next/document'
|
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
|
||||||
import { ColorModeScript } from '@chakra-ui/react'
|
import { ColorModeScript } from "@chakra-ui/react";
|
||||||
|
import theme from "../theme";
|
||||||
|
|
||||||
export default class Document extends NextDocument {
|
export default class Document extends NextDocument {
|
||||||
render() {
|
render() {
|
||||||
|
|
@ -8,11 +9,11 @@ export default class Document extends NextDocument {
|
||||||
<Head />
|
<Head />
|
||||||
<body>
|
<body>
|
||||||
{/* Make Color mode to persists when you refresh the page. */}
|
{/* Make Color mode to persists when you refresh the page. */}
|
||||||
<ColorModeScript />
|
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
</Html>
|
</Html>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { Text } from "@chakra-ui/layout";
|
||||||
|
import { Flex as Box } from "@chakra-ui/react";
|
||||||
|
import { GetServerSideProps, NextPage } from "next";
|
||||||
|
import { Session } from "next-auth";
|
||||||
|
import { getSession } from "next-auth/react";
|
||||||
|
import React from "react";
|
||||||
|
import { Container } from "../../components/Container";
|
||||||
|
import Header from "../../components/Header";
|
||||||
|
import UserData from "../../components/UserData";
|
||||||
|
|
||||||
|
interface AdminPageProps {
|
||||||
|
isAuthed: boolean;
|
||||||
|
isAdmin: boolean;
|
||||||
|
session?: Session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminPage: NextPage<AdminPageProps> = ({
|
||||||
|
isAuthed,
|
||||||
|
isAdmin,
|
||||||
|
session,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Container height="100vh">
|
||||||
|
<Header>
|
||||||
|
<UserData user={session.user} />
|
||||||
|
</Header>
|
||||||
|
<Container
|
||||||
|
height="30vh"
|
||||||
|
mt="5rem"
|
||||||
|
backgroundColor="gray.700"
|
||||||
|
width="100vw"
|
||||||
|
>
|
||||||
|
<Text>Hello</Text>
|
||||||
|
</Container>
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
|
const session = await getSession(context);
|
||||||
|
if (session) {
|
||||||
|
const isAdmin = session.user.name.includes("qpixel");
|
||||||
|
if (!isAdmin) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: "/",
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isAuthed: true,
|
||||||
|
isAdmin: isAdmin,
|
||||||
|
session: session,
|
||||||
|
} as AdminPageProps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isAdmin: false,
|
||||||
|
isAuthed: false,
|
||||||
|
} as AdminPageProps,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AdminPage;
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { GetServerSideProps, NextPage } from "next";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { Container } from "../../components/Container";
|
||||||
|
import { Text } from "@chakra-ui/react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import {
|
||||||
|
getSession,
|
||||||
|
signOut,
|
||||||
|
SignOutResponse,
|
||||||
|
useSession,
|
||||||
|
} from "next-auth/react";
|
||||||
|
|
||||||
|
const SignOutPage: NextPage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const session = useSession();
|
||||||
|
let data: SignOutResponse | null;
|
||||||
|
const signout = async () => {
|
||||||
|
data = await signOut({ redirect: false, callbackUrl: "/" });
|
||||||
|
};
|
||||||
|
if (session) {
|
||||||
|
useEffect(() => {
|
||||||
|
signout();
|
||||||
|
setTimeout(() => {
|
||||||
|
if (data) {
|
||||||
|
router.push(data.url);
|
||||||
|
} else {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Container height="100vh">
|
||||||
|
<Text>Signing out!</Text>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
|
const session = await getSession(context);
|
||||||
|
if (!session) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: "/",
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignOutPage;
|
||||||
|
|
@ -12,4 +12,10 @@ export default NextAuth({
|
||||||
jwt: {
|
jwt: {
|
||||||
secret: process.env.SECRET,
|
secret: process.env.SECRET,
|
||||||
},
|
},
|
||||||
|
callbacks: {
|
||||||
|
async session({ session, token }) {
|
||||||
|
session.user.id = token.sub;
|
||||||
|
return session;
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
import { Button, Text } from "@chakra-ui/react";
|
import { Button, Link as ChakraLink, Text } from "@chakra-ui/react";
|
||||||
import { GetServerSideProps, NextPage } from "next";
|
import { GetServerSideProps, NextPage } from "next";
|
||||||
import { getSession, signIn } from "next-auth/react";
|
import { getSession, signIn } from "next-auth/react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import Link from "next/link";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Container } from "../components/Container";
|
import { Container } from "../components/Container";
|
||||||
import { Footer } from "../components/Footer";
|
import { Footer } from "../components/Footer";
|
||||||
import { Hero } from "../components/Hero";
|
import { Hero } from "../components/Hero";
|
||||||
import { Main } from "../components/Main";
|
import { Main } from "../components/Main";
|
||||||
|
import { isDev } from "../util";
|
||||||
|
|
||||||
const Index: NextPage = () => {
|
const Index: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Watch Together</title>
|
||||||
|
</Head>
|
||||||
<Container height="100vh">
|
<Container height="100vh">
|
||||||
<Hero />
|
<Hero />
|
||||||
<Main>
|
<Main>
|
||||||
|
|
@ -16,20 +23,27 @@ const Index: NextPage = () => {
|
||||||
maxWidth="200"
|
maxWidth="200"
|
||||||
alignSelf="center"
|
alignSelf="center"
|
||||||
onClick={() => signIn("discord")}
|
onClick={() => signIn("discord")}
|
||||||
|
disabled={!isDev()}
|
||||||
>
|
>
|
||||||
Login With Discord
|
Login With Discord
|
||||||
</Button>
|
</Button>
|
||||||
</Main>
|
</Main>
|
||||||
<Footer>
|
<Footer>
|
||||||
|
<ChakraLink>
|
||||||
|
<Link href="https://velvox.dev">
|
||||||
<Text>©2022 Velvox</Text>
|
<Text>©2022 Velvox</Text>
|
||||||
|
</Link>
|
||||||
|
</ChakraLink>
|
||||||
</Footer>
|
</Footer>
|
||||||
</Container>
|
</Container>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
const session = getSession(context);
|
const session = await getSession(context);
|
||||||
if (session) {
|
const isDev = true;
|
||||||
|
if (session && !isDev) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/player",
|
destination: "/player",
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,118 @@
|
||||||
import { NextPage } from "next";
|
import { GetServerSideProps, NextPage } from "next";
|
||||||
import dynamic from "next/dynamic";
|
import { User } from "next-auth";
|
||||||
import React from "react";
|
import { getSession } from "next-auth/react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import ReactPlayer from "react-player";
|
||||||
import { Container } from "../components/Container";
|
import { Container } from "../components/Container";
|
||||||
|
import Player from "../components/Player";
|
||||||
|
import useWS from "../hooks/useWS";
|
||||||
|
import IdentityData from "../interfaces/Identity";
|
||||||
|
import { MessageTypes } from "../interfaces/IMessage";
|
||||||
|
import SetPlayheadEvent from "../interfaces/Playhead";
|
||||||
|
import SocketEvents from "../interfaces/SocketEvents";
|
||||||
|
import { isBrowser } from "../util";
|
||||||
|
import Message from "../util/Message";
|
||||||
|
import MessageUtil from "../util/MessageUtil";
|
||||||
|
|
||||||
const Player = dynamic(() => import("../components/Player"));
|
interface PlayerPageProps {
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
// types for the function
|
||||||
|
|
||||||
const PlayerPage: NextPage = () => {
|
const PlayerPage: NextPage<PlayerPageProps> = ({ user }) => {
|
||||||
|
const socket = useWS({ user });
|
||||||
|
const playerRef = useRef<ReactPlayer>();
|
||||||
|
const [id, setID] = useState<string>("");
|
||||||
|
const [identity, setIdentity] = useState<IdentityData>();
|
||||||
|
const [paused, setPaused] = useState<boolean>(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isBrowser() && typeof socket !== "undefined") {
|
||||||
|
socket?.emitter.once(SocketEvents.Identify, (e: IdentityData) => {
|
||||||
|
setID(e.playlist);
|
||||||
|
setIdentity(e);
|
||||||
|
playerRef?.current.seekTo(e.playhead);
|
||||||
|
setPaused(e.paused);
|
||||||
|
});
|
||||||
|
socket?.emitter.on(SocketEvents.SetPlayhead, (e: SetPlayheadEvent) => {
|
||||||
|
console.log(e.paused);
|
||||||
|
setPaused(e.paused);
|
||||||
|
playerRef.current.seekTo(e.playhead);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [socket]);
|
||||||
|
const onPlay = () => {
|
||||||
|
if (!identity.admin) return;
|
||||||
|
setPaused(false);
|
||||||
|
socket?.send(
|
||||||
|
MessageUtil.encode(
|
||||||
|
new Message(MessageTypes.SetPlayhead, {
|
||||||
|
playhead: playerRef.current.getCurrentTime(),
|
||||||
|
paused: false,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const onSeek = (playedSeconds: number) => {
|
||||||
|
if (!identity.admin) return;
|
||||||
|
socket.send(
|
||||||
|
MessageUtil.encode(
|
||||||
|
new Message(MessageTypes.SetPlayhead, {
|
||||||
|
playhead: playedSeconds,
|
||||||
|
paused: paused,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const onPause = () => {
|
||||||
|
console.log("running now");
|
||||||
|
if (!identity.admin) return;
|
||||||
|
setPaused(true);
|
||||||
|
socket?.send(
|
||||||
|
MessageUtil.encode(
|
||||||
|
new Message(MessageTypes.SetPlayhead, {
|
||||||
|
playhead: playerRef.current.getCurrentTime(),
|
||||||
|
paused: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Container height="100vh">
|
<>
|
||||||
<Player />
|
<Head>
|
||||||
|
<title>Watch Together</title>
|
||||||
|
</Head>
|
||||||
|
<Container height="100vh" background={"#000"}>
|
||||||
|
<Player
|
||||||
|
url={id}
|
||||||
|
onPlay={onPlay}
|
||||||
|
onPause={onPause}
|
||||||
|
onSeek={onSeek}
|
||||||
|
controls={identity?.hasController}
|
||||||
|
playing={!paused}
|
||||||
|
ref={playerRef}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
|
const session = await getSession(context);
|
||||||
|
if (!session) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: "/",
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
user: session.user,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default PlayerPage;
|
export default PlayerPage;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import IMessage, { MessageTypes } from "../interfaces/IMessage";
|
||||||
|
|
||||||
|
export default class Message implements IMessage {
|
||||||
|
constructor(public type: MessageTypes, public data?: unknown) {}
|
||||||
|
|
||||||
|
toJSON(): Record<string, unknown> {
|
||||||
|
return {
|
||||||
|
type: this.type,
|
||||||
|
data: this.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// inspired by aether/aero
|
||||||
|
|
||||||
|
import Message from "./Message";
|
||||||
|
|
||||||
|
export default class MessageUtil {
|
||||||
|
static encode(message: Message): string {
|
||||||
|
return JSON.stringify(message);
|
||||||
|
}
|
||||||
|
static decode(message: string): Message | null {
|
||||||
|
const parsed = JSON.parse(message);
|
||||||
|
if (typeof parsed.type !== "number") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Message(parsed.type, parsed.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
const useAPI = () => {};
|
|
||||||
|
|
||||||
export default useAPI;
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export function isDev() {
|
||||||
|
return process.env.NODE_ENV === "development";
|
||||||
|
}
|
||||||
|
export function isBrowser() {
|
||||||
|
return typeof window !== "undefined";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { User } from "next-auth";
|
||||||
|
import EventEmitter from "events";
|
||||||
|
import IdentityData from "../interfaces/Identity";
|
||||||
|
import { MessageTypes } from "../interfaces/IMessage";
|
||||||
|
import Message from "../util/Message";
|
||||||
|
import MessageUtil from "../util/MessageUtil";
|
||||||
|
|
||||||
|
// browser socket
|
||||||
|
// todo: write a shim for this
|
||||||
|
let Websocket: typeof WebSocket;
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
Websocket = window.WebSocket;
|
||||||
|
} else {
|
||||||
|
Websocket = require("ws");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PlayerSocket extends Websocket {
|
||||||
|
private clientID: string;
|
||||||
|
public emitter: EventEmitter;
|
||||||
|
constructor(private user: User) {
|
||||||
|
super(process.env.NEXT_PUBLIC_WS_URI);
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
this.clientID = process.env.NEXT_PUBLIC_CLIENT_ID;
|
||||||
|
this.onopen = this.onOpen;
|
||||||
|
this.onmessage = this.onMessage;
|
||||||
|
this.onclose = this.onClose;
|
||||||
|
}
|
||||||
|
close(code?: number, reason?: string): void {
|
||||||
|
this.emitter.removeAllListeners();
|
||||||
|
super.close(code, reason);
|
||||||
|
}
|
||||||
|
onMessage(evt: MessageEvent<any>) {
|
||||||
|
let message = MessageUtil.decode(evt.data);
|
||||||
|
if (message.type === MessageTypes["Ping"]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.emitter.emit(MessageTypes[message.type], message.data);
|
||||||
|
}
|
||||||
|
onOpen() {
|
||||||
|
this.send(
|
||||||
|
MessageUtil.encode(
|
||||||
|
new Message(MessageTypes.Identify, {
|
||||||
|
clientID: this.clientID,
|
||||||
|
user: {
|
||||||
|
id: this.user.id,
|
||||||
|
name: this.user.name,
|
||||||
|
},
|
||||||
|
} as IdentityData)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.emitter.emit("open");
|
||||||
|
this.pingEvent();
|
||||||
|
}
|
||||||
|
pingEvent() {
|
||||||
|
let interval = setInterval(() => {
|
||||||
|
if (!this.open) {
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("[WS] running ping event");
|
||||||
|
this.send(
|
||||||
|
MessageUtil.encode(
|
||||||
|
new Message(MessageTypes.Ping, {
|
||||||
|
clientID: this.clientID,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, 20000);
|
||||||
|
}
|
||||||
|
onClose(event: CloseEvent) {
|
||||||
|
console.log("[WS] socket connection closed");
|
||||||
|
this.emitter.emit("closed");
|
||||||
|
}
|
||||||
|
get open() {
|
||||||
|
return this.readyState === this.OPEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,8 +12,9 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve"
|
"jsx": "preserve",
|
||||||
|
"typeRoots": ["types/"]
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
"include": ["types/*.d.ts", "**/*.ts", "**/*.tsx", "next-seo.config.js"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ declare namespace NodeJS {
|
||||||
export interface ProcessEnv {
|
export interface ProcessEnv {
|
||||||
DISCORD_ID: string;
|
DISCORD_ID: string;
|
||||||
DISCORD_SECRET: string;
|
DISCORD_SECRET: string;
|
||||||
API_PORT: string;
|
|
||||||
SECRET: string;
|
SECRET: string;
|
||||||
|
NEXT_PUBLIC_CLIENT_ID: string;
|
||||||
|
NEXT_PUBLIC_WS_URI: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import NextAuth, { Session as NextSession, User as NextUser } from "next-auth";
|
||||||
|
|
||||||
|
declare module "next-auth" {
|
||||||
|
interface User extends NextUser {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
interface Session extends NextSession {
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -818,6 +818,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.3.tgz#76d6d0c3f4d16013c61e45dfca5ff1e6c31ae53c"
|
resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.3.tgz#76d6d0c3f4d16013c61e45dfca5ff1e6c31ae53c"
|
||||||
integrity sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA==
|
integrity sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA==
|
||||||
|
|
||||||
|
"@next/bundle-analyzer@^12.0.8":
|
||||||
|
version "12.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-12.0.8.tgz#a4fef1b14f8a4a87c09d97a3d909deddc88d12f8"
|
||||||
|
integrity sha512-tRwFyAkJA0h+rwt4exq31T59qo4qwp7vPoR3yC8gIpK/E5NAwafyk40aNpk4OWhiQ2IvJMFutukMzY3xl79NXA==
|
||||||
|
dependencies:
|
||||||
|
webpack-bundle-analyzer "4.3.0"
|
||||||
|
|
||||||
"@next/env@12.0.7":
|
"@next/env@12.0.7":
|
||||||
version "12.0.7"
|
version "12.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.7.tgz#316f7bd1b6b69f554d2676cfc91a16bc7e32ee79"
|
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.7.tgz#316f7bd1b6b69f554d2676cfc91a16bc7e32ee79"
|
||||||
|
|
@ -910,6 +917,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.1.tgz#ed0da773bd5f794d0603f5a5b5cee6d2354e5660"
|
resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.1.tgz#ed0da773bd5f794d0603f5a5b5cee6d2354e5660"
|
||||||
integrity sha512-mMyQ9vjpuFqePkfe5bZVIf/H3Dmk6wA8Kjxff9RcO4kqzJo+Ek9pGKwZHpeMr7Eku0QhLXMCd7fNCSnEnRMubg==
|
integrity sha512-mMyQ9vjpuFqePkfe5bZVIf/H3Dmk6wA8Kjxff9RcO4kqzJo+Ek9pGKwZHpeMr7Eku0QhLXMCd7fNCSnEnRMubg==
|
||||||
|
|
||||||
|
"@polka/url@^1.0.0-next.20":
|
||||||
|
version "1.0.0-next.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
|
||||||
|
integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
|
||||||
|
|
||||||
"@popperjs/core@^2.9.3":
|
"@popperjs/core@^2.9.3":
|
||||||
version "2.11.0"
|
version "2.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.0.tgz#6734f8ebc106a0860dff7f92bf90df193f0935d7"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.0.tgz#6734f8ebc106a0860dff7f92bf90df193f0935d7"
|
||||||
|
|
@ -942,6 +954,11 @@
|
||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
|
"@types/events@^3.0.0":
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||||
|
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
||||||
|
|
||||||
"@types/lodash.mergewith@4.6.6":
|
"@types/lodash.mergewith@4.6.6":
|
||||||
version "4.6.6"
|
version "4.6.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10"
|
resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10"
|
||||||
|
|
@ -1000,6 +1017,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf"
|
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf"
|
||||||
integrity sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw==
|
integrity sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw==
|
||||||
|
|
||||||
|
"@types/uuid@^8.3.4":
|
||||||
|
version "8.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
||||||
|
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
|
||||||
|
|
||||||
"@types/warning@^3.0.0":
|
"@types/warning@^3.0.0":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
|
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
|
||||||
|
|
@ -1012,11 +1034,28 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/ws@^8.2.2":
|
||||||
|
version "8.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21"
|
||||||
|
integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
acorn-walk@^8.0.0:
|
||||||
|
version "8.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||||
|
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||||
|
|
||||||
acorn@8.5.0:
|
acorn@8.5.0:
|
||||||
version "8.5.0"
|
version "8.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
|
||||||
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
|
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
|
||||||
|
|
||||||
|
acorn@^8.0.4:
|
||||||
|
version "8.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
|
||||||
|
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
|
||||||
|
|
||||||
anser@1.4.9:
|
anser@1.4.9:
|
||||||
version "1.4.9"
|
version "1.4.9"
|
||||||
resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760"
|
resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760"
|
||||||
|
|
@ -1212,13 +1251,6 @@ buffer@5.6.0:
|
||||||
base64-js "^1.0.2"
|
base64-js "^1.0.2"
|
||||||
ieee754 "^1.1.4"
|
ieee754 "^1.1.4"
|
||||||
|
|
||||||
bufferutil@^4.0.1:
|
|
||||||
version "4.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.6.tgz#ebd6c67c7922a0e902f053e5d8be5ec850e48433"
|
|
||||||
integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==
|
|
||||||
dependencies:
|
|
||||||
node-gyp-build "^4.3.0"
|
|
||||||
|
|
||||||
builtin-status-codes@^3.0.0:
|
builtin-status-codes@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||||
|
|
@ -1264,6 +1296,14 @@ chalk@4.0.0:
|
||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
|
chalk@^4.1.0:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||||
|
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^4.1.0"
|
||||||
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chokidar@3.5.1:
|
chokidar@3.5.1:
|
||||||
version "3.5.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
|
||||||
|
|
@ -1321,6 +1361,11 @@ colorette@^1.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
|
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
|
||||||
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
|
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
|
||||||
|
|
||||||
|
commander@^6.2.0:
|
||||||
|
version "6.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||||
|
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||||
|
|
||||||
commondir@^1.0.1:
|
commondir@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||||
|
|
@ -1331,6 +1376,11 @@ compute-scroll-into-view@1.0.14:
|
||||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz#80e3ebb25d6aa89f42e533956cb4b16a04cfe759"
|
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz#80e3ebb25d6aa89f42e533956cb4b16a04cfe759"
|
||||||
integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==
|
integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==
|
||||||
|
|
||||||
|
consola@^2.15.3:
|
||||||
|
version "2.15.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550"
|
||||||
|
integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==
|
||||||
|
|
||||||
constants-browserify@1.0.0:
|
constants-browserify@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
|
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
|
||||||
|
|
@ -1404,6 +1454,22 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
sha.js "^2.4.8"
|
sha.js "^2.4.8"
|
||||||
|
|
||||||
|
cross-env@^7.0.3:
|
||||||
|
version "7.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
|
||||||
|
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
|
||||||
|
dependencies:
|
||||||
|
cross-spawn "^7.0.1"
|
||||||
|
|
||||||
|
cross-spawn@^7.0.1:
|
||||||
|
version "7.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||||
|
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||||
|
dependencies:
|
||||||
|
path-key "^3.1.0"
|
||||||
|
shebang-command "^2.0.0"
|
||||||
|
which "^2.0.1"
|
||||||
|
|
||||||
crypto-browserify@3.12.0:
|
crypto-browserify@3.12.0:
|
||||||
version "3.12.0"
|
version "3.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||||
|
|
@ -1452,20 +1518,12 @@ csstype@^3.0.2, csstype@^3.0.9:
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
|
||||||
integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
|
integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
|
||||||
|
|
||||||
d@1, d@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
|
|
||||||
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
|
|
||||||
dependencies:
|
|
||||||
es5-ext "^0.10.50"
|
|
||||||
type "^1.0.1"
|
|
||||||
|
|
||||||
data-uri-to-buffer@3.0.1:
|
data-uri-to-buffer@3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636"
|
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636"
|
||||||
integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==
|
integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==
|
||||||
|
|
||||||
debug@2, debug@^2.2.0:
|
debug@2:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||||
|
|
@ -1516,6 +1574,11 @@ domain-browser@4.19.0:
|
||||||
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1"
|
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1"
|
||||||
integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ==
|
integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ==
|
||||||
|
|
||||||
|
duplexer@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
|
||||||
|
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
|
||||||
|
|
||||||
electron-to-chromium@^1.3.723:
|
electron-to-chromium@^1.3.723:
|
||||||
version "1.4.26"
|
version "1.4.26"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.26.tgz#d71b9da220543cf10614a576c3d5ebbe43d96efb"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.26.tgz#d71b9da220543cf10614a576c3d5ebbe43d96efb"
|
||||||
|
|
@ -1588,37 +1651,11 @@ es-to-primitive@^1.2.1:
|
||||||
is-date-object "^1.0.1"
|
is-date-object "^1.0.1"
|
||||||
is-symbol "^1.0.2"
|
is-symbol "^1.0.2"
|
||||||
|
|
||||||
es5-ext@^0.10.35, es5-ext@^0.10.50:
|
|
||||||
version "0.10.53"
|
|
||||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1"
|
|
||||||
integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==
|
|
||||||
dependencies:
|
|
||||||
es6-iterator "~2.0.3"
|
|
||||||
es6-symbol "~3.1.3"
|
|
||||||
next-tick "~1.0.0"
|
|
||||||
|
|
||||||
es6-iterator@~2.0.3:
|
|
||||||
version "2.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
|
||||||
integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
|
|
||||||
dependencies:
|
|
||||||
d "1"
|
|
||||||
es5-ext "^0.10.35"
|
|
||||||
es6-symbol "^3.1.1"
|
|
||||||
|
|
||||||
es6-object-assign@^1.1.0:
|
es6-object-assign@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||||
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||||
|
|
||||||
es6-symbol@^3.1.1, es6-symbol@~3.1.3:
|
|
||||||
version "3.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
|
|
||||||
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
|
|
||||||
dependencies:
|
|
||||||
d "^1.0.1"
|
|
||||||
ext "^1.1.2"
|
|
||||||
|
|
||||||
escalade@^3.1.1:
|
escalade@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
|
|
@ -1639,7 +1676,7 @@ etag@1.8.1:
|
||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||||
|
|
||||||
events@3.3.0:
|
events@3.3.0, events@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||||
|
|
@ -1652,13 +1689,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
|
||||||
md5.js "^1.3.4"
|
md5.js "^1.3.4"
|
||||||
safe-buffer "^5.1.1"
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
ext@^1.1.2:
|
|
||||||
version "1.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52"
|
|
||||||
integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==
|
|
||||||
dependencies:
|
|
||||||
type "^2.5.0"
|
|
||||||
|
|
||||||
fill-range@^7.0.1:
|
fill-range@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||||
|
|
@ -1776,6 +1806,13 @@ graceful-fs@^4.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
||||||
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
|
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
|
||||||
|
|
||||||
|
gzip-size@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
||||||
|
integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==
|
||||||
|
dependencies:
|
||||||
|
duplexer "^0.1.2"
|
||||||
|
|
||||||
has-bigints@^1.0.1:
|
has-bigints@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
|
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
|
||||||
|
|
@ -2060,11 +2097,6 @@ is-typed-array@^1.1.3, is-typed-array@^1.1.7:
|
||||||
foreach "^2.0.5"
|
foreach "^2.0.5"
|
||||||
has-tostringtag "^1.0.0"
|
has-tostringtag "^1.0.0"
|
||||||
|
|
||||||
is-typedarray@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
|
||||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
|
||||||
|
|
||||||
is-weakref@^1.0.1:
|
is-weakref@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
|
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
|
||||||
|
|
@ -2072,6 +2104,11 @@ is-weakref@^1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind "^1.0.2"
|
call-bind "^1.0.2"
|
||||||
|
|
||||||
|
isexe@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
|
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||||
|
|
||||||
jest-worker@27.0.0-next.5:
|
jest-worker@27.0.0-next.5:
|
||||||
version "27.0.0-next.5"
|
version "27.0.0-next.5"
|
||||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28"
|
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28"
|
||||||
|
|
@ -2139,6 +2176,11 @@ lodash.sortby@^4.7.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||||
|
|
||||||
|
lodash@^4.17.20:
|
||||||
|
version "4.17.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
|
|
@ -2202,6 +2244,11 @@ minimist@^1.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||||
|
|
||||||
|
mrmime@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b"
|
||||||
|
integrity sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
|
@ -2227,11 +2274,6 @@ next-auth@^4.1.2:
|
||||||
preact-render-to-string "^5.1.19"
|
preact-render-to-string "^5.1.19"
|
||||||
uuid "^8.3.2"
|
uuid "^8.3.2"
|
||||||
|
|
||||||
next-tick@~1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
|
||||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
|
||||||
|
|
||||||
next@latest:
|
next@latest:
|
||||||
version "12.0.7"
|
version "12.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-12.0.7.tgz#33ebf229b81b06e583ab5ae7613cffe1ca2103fc"
|
resolved "https://registry.yarnpkg.com/next/-/next-12.0.7.tgz#33ebf229b81b06e583ab5ae7613cffe1ca2103fc"
|
||||||
|
|
@ -2304,11 +2346,6 @@ node-fetch@2.6.1:
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
||||||
node-gyp-build@^4.3.0:
|
|
||||||
version "4.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
|
||||||
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
|
|
||||||
|
|
||||||
node-html-parser@1.4.9:
|
node-html-parser@1.4.9:
|
||||||
version "1.4.9"
|
version "1.4.9"
|
||||||
resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c"
|
resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c"
|
||||||
|
|
@ -2374,6 +2411,11 @@ oidc-token-hash@^5.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6"
|
resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6"
|
||||||
integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==
|
integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==
|
||||||
|
|
||||||
|
opener@^1.5.2:
|
||||||
|
version "1.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
|
||||||
|
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
|
||||||
|
|
||||||
openid-client@^5.1.0:
|
openid-client@^5.1.0:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.1.2.tgz#a80cc6a7d8d7159ad97c51781338f5a954396bba"
|
resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.1.2.tgz#a80cc6a7d8d7159ad97c51781338f5a954396bba"
|
||||||
|
|
@ -2458,6 +2500,11 @@ path-exists@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||||
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||||
|
|
||||||
|
path-key@^3.1.0:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||||
|
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||||
|
|
||||||
path-parse@^1.0.6:
|
path-parse@^1.0.6:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
|
|
@ -2788,6 +2835,18 @@ sha.js@^2.4.0, sha.js@^2.4.8:
|
||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
shebang-command@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||||
|
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
|
||||||
|
dependencies:
|
||||||
|
shebang-regex "^3.0.0"
|
||||||
|
|
||||||
|
shebang-regex@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||||
|
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||||
|
|
||||||
shell-quote@1.7.3:
|
shell-quote@1.7.3:
|
||||||
version "1.7.3"
|
version "1.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
|
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
|
||||||
|
|
@ -2802,6 +2861,15 @@ side-channel@^1.0.4:
|
||||||
get-intrinsic "^1.0.2"
|
get-intrinsic "^1.0.2"
|
||||||
object-inspect "^1.9.0"
|
object-inspect "^1.9.0"
|
||||||
|
|
||||||
|
sirv@^1.0.7:
|
||||||
|
version "1.0.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
|
||||||
|
integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==
|
||||||
|
dependencies:
|
||||||
|
"@polka/url" "^1.0.0-next.20"
|
||||||
|
mrmime "^1.0.0"
|
||||||
|
totalist "^1.0.0"
|
||||||
|
|
||||||
source-map@0.7.3:
|
source-map@0.7.3:
|
||||||
version "0.7.3"
|
version "0.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||||
|
|
@ -2993,6 +3061,11 @@ toidentifier@1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||||
|
|
||||||
|
totalist@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
|
||||||
|
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
|
||||||
|
|
||||||
tr46@^1.0.1:
|
tr46@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
|
||||||
|
|
@ -3020,23 +3093,6 @@ type-fest@^0.7.1:
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
|
||||||
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
|
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
|
||||||
|
|
||||||
type@^1.0.1:
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
|
|
||||||
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
|
|
||||||
|
|
||||||
type@^2.5.0:
|
|
||||||
version "2.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d"
|
|
||||||
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
|
|
||||||
|
|
||||||
typedarray-to-buffer@^3.1.5:
|
|
||||||
version "3.1.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
|
||||||
integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
|
|
||||||
dependencies:
|
|
||||||
is-typedarray "^1.0.0"
|
|
||||||
|
|
||||||
typescript@4.3.2:
|
typescript@4.3.2:
|
||||||
version "4.3.2"
|
version "4.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
||||||
|
|
@ -3077,13 +3133,6 @@ use-subscription@1.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
utf-8-validate@^5.0.2:
|
|
||||||
version "5.0.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.8.tgz#4a735a61661dbb1c59a0868c397d2fe263f14e58"
|
|
||||||
integrity sha512-k4dW/Qja1BYDl2qD4tOMB9PFVha/UJtxTc1cXYOe3WwA/2m0Yn4qB7wLMpJyLJ/7DR0XnTut3HsCSzDT4ZvKgA==
|
|
||||||
dependencies:
|
|
||||||
node-gyp-build "^4.3.0"
|
|
||||||
|
|
||||||
util-deprecate@^1.0.1:
|
util-deprecate@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
|
@ -3131,17 +3180,20 @@ webidl-conversions@^4.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||||
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
||||||
|
|
||||||
websocket@^1.0.34:
|
webpack-bundle-analyzer@4.3.0:
|
||||||
version "1.0.34"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
|
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b"
|
||||||
integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==
|
integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA==
|
||||||
dependencies:
|
dependencies:
|
||||||
bufferutil "^4.0.1"
|
acorn "^8.0.4"
|
||||||
debug "^2.2.0"
|
acorn-walk "^8.0.0"
|
||||||
es5-ext "^0.10.50"
|
chalk "^4.1.0"
|
||||||
typedarray-to-buffer "^3.1.5"
|
commander "^6.2.0"
|
||||||
utf-8-validate "^5.0.2"
|
gzip-size "^6.0.0"
|
||||||
yaeti "^0.0.6"
|
lodash "^4.17.20"
|
||||||
|
opener "^1.5.2"
|
||||||
|
sirv "^1.0.7"
|
||||||
|
ws "^7.3.1"
|
||||||
|
|
||||||
whatwg-url@^7.0.0:
|
whatwg-url@^7.0.0:
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
|
|
@ -3175,16 +3227,28 @@ which-typed-array@^1.1.2:
|
||||||
has-tostringtag "^1.0.0"
|
has-tostringtag "^1.0.0"
|
||||||
is-typed-array "^1.1.7"
|
is-typed-array "^1.1.7"
|
||||||
|
|
||||||
|
which@^2.0.1:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||||
|
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||||
|
dependencies:
|
||||||
|
isexe "^2.0.0"
|
||||||
|
|
||||||
|
ws@^7.3.1:
|
||||||
|
version "7.5.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
|
||||||
|
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
|
||||||
|
|
||||||
|
ws@^8.4.2:
|
||||||
|
version "8.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b"
|
||||||
|
integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==
|
||||||
|
|
||||||
xtend@^4.0.2:
|
xtend@^4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
yaeti@^0.0.6:
|
|
||||||
version "0.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
|
||||||
integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=
|
|
||||||
|
|
||||||
yallist@^4.0.0:
|
yallist@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue