diff --git a/handlers/tunnel/plugin.go b/handlers/tunnel/plugin.go index 484f58f..3c73847 100644 --- a/handlers/tunnel/plugin.go +++ b/handlers/tunnel/plugin.go @@ -1,13 +1,14 @@ package tunnel import ( + "context" "net" kitlog "github.com/go-kit/kit/log" "go.cryptoscope.co/muxrpc/v2" "go.cryptoscope.co/muxrpc/v2/typemux" - refs "go.mindeco.de/ssb-refs" + "go.mindeco.de/ssb-rooms/internal/broadcasts" "go.mindeco.de/ssb-rooms/internal/maybemuxrpc" ) @@ -36,11 +37,18 @@ func (plugin) Authorize(net.Conn) bool { return true } } */ -func New(log kitlog.Logger, self refs.FeedRef) maybemuxrpc.Plugin { +func New(log kitlog.Logger, ctx context.Context) maybemuxrpc.Plugin { mux := typemux.New(log) - var rs roomState + var rs = new(roomState) rs.logger = log + rs.updater, rs.broadcaster = broadcasts.NewRoomChanger() + rs.rooms = make(roomsStateMap) + + go rs.stateTicker(ctx) + + // so far just lobby (v1 rooms) + rs.rooms["lobby"] = make(roomStateMap) mux.RegisterAsync(append(method, "isRoom"), typemux.AsyncFunc(rs.isRoom)) mux.RegisterAsync(append(method, "ping"), typemux.AsyncFunc(rs.ping)) diff --git a/handlers/tunnel/state.go b/handlers/tunnel/state.go index 38d3a91..db815e8 100644 --- a/handlers/tunnel/state.go +++ b/handlers/tunnel/state.go @@ -2,40 +2,137 @@ package tunnel import ( "context" - "fmt" + "encoding/json" + "sync" "time" + "go.mindeco.de/ssb-rooms/internal/network" + kitlog "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "go.cryptoscope.co/muxrpc/v2" + "go.mindeco.de/ssb-rooms/internal/broadcasts" ) type roomState struct { logger kitlog.Logger + + updater broadcasts.RoomChangeSink + broadcaster *broadcasts.RoomChangeBroadcast + + roomsMu sync.Mutex + rooms roomsStateMap } -func (rs roomState) isRoom(context.Context, *muxrpc.Request) (interface{}, error) { +func (rs *roomState) stateTicker(ctx context.Context) { + tick := time.NewTicker(1 * time.Second) + for { + select { + case <-ctx.Done(): + tick.Stop() + return + + case <-tick.C: + } + rs.roomsMu.Lock() + for room, members := range rs.rooms { + level.Info(rs.logger).Log("room", room, "cnt", len(members)) + for who := range members { + level.Info(rs.logger).Log("room", room, "feed", who) + } + } + rs.roomsMu.Unlock() + } +} + +// layout is map[room-name]map[canonical feedref]client-handle +type roomsStateMap map[string]roomStateMap + +// roomStateMap is a single room +type roomStateMap map[string]muxrpc.Endpoint + +func (rs *roomState) isRoom(context.Context, *muxrpc.Request) (interface{}, error) { level.Debug(rs.logger).Log("called", "isRoom") return true, nil } -func (rs roomState) ping(context.Context, *muxrpc.Request) (interface{}, error) { +func (rs *roomState) ping(context.Context, *muxrpc.Request) (interface{}, error) { now := time.Now().UnixNano() / 1000 level.Debug(rs.logger).Log("called", "ping") return now, nil } -func (rs roomState) announce(context.Context, *muxrpc.Request) (interface{}, error) { +func (rs *roomState) announce(_ context.Context, req *muxrpc.Request) (interface{}, error) { level.Debug(rs.logger).Log("called", "announce") - return nil, fmt.Errorf("TODO:announce") + ref, err := network.GetFeedRefFromAddr(req.RemoteAddr()) + if err != nil { + return nil, err + } + + rs.roomsMu.Lock() + rs.updater.Update(broadcasts.RoomChange{ + Op: "joined", + Who: *ref, + }) + + // add ref to lobby + rs.rooms["lobby"][ref.Ref()] = req.Endpoint() + members := len(rs.rooms["lobby"]) + rs.roomsMu.Unlock() + + return RoomUpdate{"joined", true, uint(members)}, nil } -func (rs roomState) leave(context.Context, *muxrpc.Request) (interface{}, error) { - level.Debug(rs.logger).Log("called", "leave") - return nil, fmt.Errorf("TODO:leave") +type RoomUpdate struct { + Action string + Success bool + Members uint } -func (rs roomState) endpoints(context.Context, *muxrpc.Request, *muxrpc.ByteSink, muxrpc.Endpoint) error { +func (rs *roomState) leave(_ context.Context, req *muxrpc.Request) (interface{}, error) { + ref, err := network.GetFeedRefFromAddr(req.RemoteAddr()) + if err != nil { + return nil, err + } + + rs.roomsMu.Lock() + rs.updater.Update(broadcasts.RoomChange{ + Op: "left", + Who: *ref, + }) + + // add ref to lobby + delete(rs.rooms["lobby"], ref.Ref()) + members := len(rs.rooms["lobby"]) + rs.roomsMu.Unlock() + + return RoomUpdate{"left", true, uint(members)}, nil +} + +func (rs *roomState) endpoints(_ context.Context, req *muxrpc.Request, snk *muxrpc.ByteSink, edp muxrpc.Endpoint) error { level.Debug(rs.logger).Log("called", "endpoints") - return fmt.Errorf("TODO:endpoints") + rs.broadcaster.Register(newForwarder(snk)) + return nil +} + +type updateForwarder struct { + snk *muxrpc.ByteSink + enc *json.Encoder +} + +func newForwarder(snk *muxrpc.ByteSink) updateForwarder { + enc := json.NewEncoder(snk) + snk.SetEncoding(muxrpc.TypeJSON) + return updateForwarder{ + snk: snk, + enc: enc, + } +} + +func (uf updateForwarder) Update(rc broadcasts.RoomChange) error { + return uf.enc.Encode(rc) +} + +func (uf updateForwarder) Close() error { + return uf.snk.Close() } diff --git a/internal/broadcasts/broadcast_test.go b/internal/broadcasts/broadcast_test.go new file mode 100644 index 0000000..ed37a2a --- /dev/null +++ b/internal/broadcasts/broadcast_test.go @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: MIT + +package broadcasts + +import ( + "errors" + "fmt" +) + +type testPrinter struct{} + +func (tp testPrinter) Update(rc RoomChange) error { + fmt.Println("test:", rc.Op, rc.Who.Ref()) + return nil +} + +func (tp testPrinter) Close() error { return nil } + +func ExampleBroadcast() { + sink, bcast := NewRoomChanger() + defer sink.Close() + + var p1, p2 testPrinter + + closeSink := bcast.Register(p1) + defer closeSink() + closeSink = bcast.Register(p2) + defer closeSink() + + var rc RoomChange + rc.Who.Algo = "dummy" + rc.Who.ID = []byte{0, 0, 0, 0} + rc.Op = "joined" + sink.Update(rc) + + // Output: + // test: joined @AAAAAA==.dummy + // test: joined @AAAAAA==.dummy +} + +func ExampleBroadcastCanceled() { + sink, bcast := NewRoomChanger() + defer sink.Close() + + var p1, p2 testPrinter + + closeSink := bcast.Register(p1) + defer closeSink() + closeSink = bcast.Register(p2) + + var rc RoomChange + rc.Who.Algo = "dummy" + rc.Who.ID = []byte{0, 0, 0, 0} + rc.Op = "joined" + + closeSink() + + sink.Update(rc) + + // Output: + // test: joined @AAAAAA==.dummy +} + +type erroringPrinter struct{} + +func (tp erroringPrinter) Update(rc RoomChange) error { + fmt.Println("failed:", rc.Op, rc.Who.Ref()) + return errors.New("nope") +} + +func (tp erroringPrinter) Close() error { return nil } + +func ExampleBroadcastOneErrs() { + sink, bcast := NewRoomChanger() + defer sink.Close() + + var p1 testPrinter + var p2 erroringPrinter + + closeSink := bcast.Register(p1) + defer closeSink() + closeSink = bcast.Register(p2) + defer closeSink() + + var rc RoomChange + rc.Who.Algo = "dummy" + rc.Who.ID = []byte{0, 0, 0, 0} + rc.Op = "joined" + + sink.Update(rc) + + rc.Op = "left" + sink.Update(rc) + + // Output: + // test: joined @AAAAAA==.dummy + // failed: joined @AAAAAA==.dummy + // test: left @AAAAAA==.dummy +} + +/* +type expectedEOSErr struct{ v interface{} } + +func (err expectedEOSErr) Error() string { + return fmt.Sprintf("expected end of stream but got %q", err.v) +} + +func (err expectedEOSErr) IsExpectedEOS() bool { + return true +} + +func IsExpectedEOS(err error) bool { + _, ok := err.(expectedEOSErr) + return ok +} + +func TestBroadcast(t *testing.T) { + type testcase struct { + rx, tx []interface{} + } + + test := func(tc testcase) { + if tc.rx == nil { + tc.rx = tc.tx + } + + sink, bcast := NewRoomChanger() + + mkSink := func() Sink { + var ( + closed bool + i int + ) + + return FuncSink(func(ctx context.Context, v interface{}, err error) error { + if err != nil { + if err != (EOS{}) { + t.Log("closed with non-EOF error:", err) + } + + if closed { + return fmt.Errorf("sink already closed") + } + + if i != len(tc.rx) { + return fmt.Errorf("early close at i=%v", i) + } + + closed = true + return nil + } + + if i >= len(tc.rx) { + return expectedEOSErr{v} + } + + if v != tc.rx[i] { + return fmt.Errorf("expected value %v but got %v", tc.rx[i], v) + } + + i++ + + return nil + }) + } + + cancelReg1 := bcast.Register(mkSink()) + cancelReg2 := bcast.Register(mkSink()) + + defer cancelReg1() + defer cancelReg2() + + for j, v := range tc.tx { + err := sink.Pour(context.TODO(), v) + + if len(tc.tx) == len(tc.rx) { + if err != nil { + t.Errorf("expected nil error but got %#v", err) + } + } else if len(tc.tx) > len(tc.rx) { + if j >= len(tc.rx) { + merr, ok := err.(*multierror.Error) + if ok { + for _, err := range merr.Errors { + if !IsExpectedEOS(err) { + t.Errorf("expected an expectedEOS error, but got %v", err) + } + } + } else { + if !IsExpectedEOS(err) { + t.Errorf("expected an expectedEOS error, but got %v", err) + } + } + } else { + if err != nil { + t.Errorf("expected nil error but got %#v", err) + } + } + } + } + + err := sink.Close() + if err != nil { + t.Errorf("expected nil error but got %s", err) + } + } + + cases := []testcase{ + {tx: []interface{}{1, 2, 3}}, + {tx: []interface{}{}}, + {tx: []interface{}{nil, 0, ""}}, + {tx: []interface{}{nil, 0, ""}, rx: []interface{}{nil, 0}}, + } + + for _, tc := range cases { + test(tc) + } + +} +*/ diff --git a/internal/broadcasts/room_change.go b/internal/broadcasts/room_change.go new file mode 100644 index 0000000..7dacea7 --- /dev/null +++ b/internal/broadcasts/room_change.go @@ -0,0 +1,105 @@ +package broadcasts + +import ( + "io" + "sync" + + "github.com/hashicorp/go-multierror" + refs "go.mindeco.de/ssb-refs" +) + +type RoomChange struct { + Op string + Who refs.FeedRef +} + +type RoomChangeSink interface { + Update(value RoomChange) error + io.Closer +} + +// NewRoomChanger returns the Sink, to write to the broadcaster, and the new +// broadcast instance. +func NewRoomChanger() (RoomChangeSink, *RoomChangeBroadcast) { + bcst := RoomChangeBroadcast{ + mu: &sync.Mutex{}, + sinks: make(map[*RoomChangeSink]struct{}), + } + + return (*broadcastSink)(&bcst), &bcst +} + +// RoomChangeBroadcast is an interface for registering one or more Sinks to recieve +// updates. +type RoomChangeBroadcast struct { + mu *sync.Mutex + sinks map[*RoomChangeSink]struct{} +} + +// Register a Sink for updates to be sent. also returns +func (bcst *RoomChangeBroadcast) Register(sink RoomChangeSink) func() { + bcst.mu.Lock() + defer bcst.mu.Unlock() + bcst.sinks[&sink] = struct{}{} + + return func() { + bcst.mu.Lock() + defer bcst.mu.Unlock() + delete(bcst.sinks, &sink) + sink.Close() + } +} + +type broadcastSink RoomChangeBroadcast + +// Pour implements the Sink interface. +func (bcst *broadcastSink) Update(rc RoomChange) error { + + bcst.mu.Lock() + for s := range bcst.sinks { + err := (*s).Update(rc) + if err != nil { + delete(bcst.sinks, s) + } + } + bcst.mu.Unlock() + + return nil +} + +// Close implements the Sink interface. +func (bcst *broadcastSink) Close() error { + var sinks []RoomChangeSink + + bcst.mu.Lock() + defer bcst.mu.Unlock() + + sinks = make([]RoomChangeSink, 0, len(bcst.sinks)) + + for sink := range bcst.sinks { + sinks = append(sinks, *sink) + } + + var ( + wg sync.WaitGroup + merr *multierror.Error + ) + + // might be fine without the waitgroup and concurrency + + wg.Add(len(sinks)) + for _, sink_ := range sinks { + go func(sink RoomChangeSink) { + defer wg.Done() + + err := sink.Close() + if err != nil { + merr = multierror.Append(merr, err) + return + } + }(sink_) + } + wg.Wait() + + return merr.ErrorOrNil() +} diff --git a/internal/maybemod/multicloser/multicloser.go b/internal/maybemod/multicloser/multicloser.go index 7553192..47f6e96 100644 --- a/internal/maybemod/multicloser/multicloser.go +++ b/internal/maybemod/multicloser/multicloser.go @@ -5,8 +5,9 @@ package multicloser import ( "fmt" "io" - "strings" "sync" + + "go.mindeco.de/ssb-rooms/internal/maybemod/multierror" ) type Closer struct { @@ -44,23 +45,5 @@ func (mc *Closer) Close() error { return nil } - return errList{errs: errs} -} - -type errList struct { - errs []error -} - -func (el errList) Error() string { - var str strings.Builder - - if n := len(el.errs); n > 0 { - fmt.Fprintf(&str, "multiple errors(%d): ", n) - } - for i, err := range el.errs { - fmt.Fprintf(&str, "(%d): ", i) - str.WriteString(err.Error() + " - ") - } - - return str.String() + return multierror.List{Errs: errs} } diff --git a/internal/maybemod/multierror/multierr.go b/internal/maybemod/multierror/multierr.go new file mode 100644 index 0000000..b4af97f --- /dev/null +++ b/internal/maybemod/multierror/multierr.go @@ -0,0 +1,23 @@ +package multierror + +import ( + "fmt" + "strings" +) + +// List contains a list of errors +type List struct{ Errs []error } + +func (el List) Error() string { + var str strings.Builder + + if n := len(el.Errs); n > 0 { + fmt.Fprintf(&str, "multiple errors(%d): ", n) + } + for i, err := range el.Errs { + fmt.Fprintf(&str, "(%d): ", i) + str.WriteString(err.Error() + " - ") + } + + return str.String() +} diff --git a/roomsrv/init_network.go b/roomsrv/init_network.go index c207b1c..4a8df18 100644 --- a/roomsrv/init_network.go +++ b/roomsrv/init_network.go @@ -41,7 +41,7 @@ func (s *Server) initNetwork() error { } // whoami - whoami := whoami.New(kitlog.With(s.logger, "unit", "whoami"), s.keyPair.Feed) + whoami := whoami.New(kitlog.With(s.logger, "unit", "whoami"), s.Whoami()) s.public.Register(whoami) s.master.Register(whoami) @@ -49,7 +49,7 @@ func (s *Server) initNetwork() error { // s.master.Register(replicate.NewPlug(s.Users)) - tunnelPlug := tunnel.New(kitlog.With(s.logger, "unit", "tunnel"), s.keyPair.Feed) + tunnelPlug := tunnel.New(kitlog.With(s.logger, "unit", "tunnel"), s.rootCtx) s.public.Register(tunnelPlug) // tcp+shs diff --git a/roomsrv/manifest.go b/roomsrv/manifest.go index 794bf86..2352d35 100644 --- a/roomsrv/manifest.go +++ b/roomsrv/manifest.go @@ -30,6 +30,15 @@ func (h manifestHandler) HandleCall(ctx context.Context, req *muxrpc.Request, ed } } +func init() { + if !json.Valid([]byte(manifest)) { + manifestMap := make(map[string]interface{}) + err := json.Unmarshal([]byte(manifest), &manifestMap) + fmt.Println(err) + panic("manifest blob is broken json") + } +} + // this is a very simple hardcoded manifest.json dump which oasis' ssb-client expects to do it's magic. const manifest manifestHandler = ` { @@ -43,7 +52,7 @@ const manifest manifestHandler = ` "connect": "duplex", "endpoints": "source", "isRoom": "async", - "ping": "sync", + "ping": "sync" } }` diff --git a/test/go/simple_test.go b/test/go/simple_test.go index fb7e82b..5ddbc9a 100644 --- a/test/go/simple_test.go +++ b/test/go/simple_test.go @@ -3,6 +3,7 @@ package go_test import ( "context" "crypto/rand" + "fmt" "testing" "time" @@ -15,14 +16,10 @@ import ( "go.mindeco.de/ssb-rooms/roomsrv" ) -func TestTunnelServerSimple(t *testing.T) { - r := require.New(t) - a := assert.New(t) - - // defer leakcheck.Check(t) +func createServerAndBots(t *testing.T, ctx context.Context, count uint) []*roomsrv.Server { testInit(t) + r := require.New(t) - ctx, cancel := context.WithCancel(context.Background()) botgroup, ctx := errgroup.WithContext(ctx) bs := newBotServer(ctx, mainLog) @@ -34,17 +31,42 @@ func TestTunnelServerSimple(t *testing.T) { roomsrv.WithAppKey(appKey), roomsrv.WithContext(ctx), } + theBots := []*roomsrv.Server{} serv := makeNamedTestBot(t, "srv", netOpts) botgroup.Go(bs.Serve(serv)) + theBots = append(theBots, serv) - botA := makeNamedTestBot(t, "B", netOpts) - botgroup.Go(bs.Serve(botA)) + for i := uint(1); i < count+1; i++ { + botI := makeNamedTestBot(t, fmt.Sprintf("%d", i), netOpts) + botgroup.Go(bs.Serve(botI)) + theBots = append(theBots, botI) + } - botB := makeNamedTestBot(t, "C", netOpts) - botgroup.Go(bs.Serve(botB)) + t.Cleanup(func() { + time.Sleep(1 * time.Second) + for _, bot := range theBots { + bot.Shutdown() + r.NoError(bot.Close()) + } + r.NoError(botgroup.Wait()) + }) - theBots := []*roomsrv.Server{serv, botA, botB} + return theBots +} + +func TestTunnelServerSimple(t *testing.T) { + // defer leakcheck.Check(t) + ctx, cancel := context.WithCancel(context.Background()) + theBots := createServerAndBots(t, ctx, 2) + + r := require.New(t) + a := assert.New(t) + + serv := theBots[0] + + botA := theBots[1] + botB := theBots[2] // only allow B to dial A serv.Allow(botA.Whoami(), true) @@ -101,10 +123,113 @@ func TestTunnelServerSimple(t *testing.T) { // cleanup cancel() - time.Sleep(1 * time.Second) - for _, bot := range theBots { - bot.Shutdown() - r.NoError(bot.Close()) - } - r.NoError(botgroup.Wait()) + +} + +func TestRoomAnnounce(t *testing.T) { + // defer leakcheck.Check(t) + ctx, cancel := context.WithCancel(context.Background()) + theBots := createServerAndBots(t, ctx, 2) + + r := require.New(t) + a := assert.New(t) + + serv := theBots[0] + + botA := theBots[1] + botB := theBots[2] + + // only allow B to dial A + serv.Allow(botA.Whoami(), true) + serv.Allow(botB.Whoami(), true) + + // allow bots to dial the remote + botA.Allow(serv.Whoami(), true) + botB.Allow(serv.Whoami(), true) + + // should work (we allowed A) + err := botA.Network.Connect(ctx, serv.Network.GetListenAddr()) + r.NoError(err, "connect A to the Server") + + // shouldn't work (we did not allowed A) + err = botB.Network.Connect(ctx, serv.Network.GetListenAddr()) + r.NoError(err, "connect B to the Server") // we dont see an error because it just establishes the tcp connection + + t.Log("letting handshaking settle..") + time.Sleep(1 * time.Second) + + var srvWho struct { + ID refs.FeedRef + } + edpOfA, has := botA.Network.GetEndpointFor(serv.Whoami()) + r.True(has, "botA has no endpoint for the server") + + edpOfB, has := botB.Network.GetEndpointFor(serv.Whoami()) + r.True(has, "botB has no endpoint for the server!") + + err = edpOfA.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"}) + r.NoError(err) + a.True(serv.Whoami().Equal(&srvWho.ID)) + + err = edpOfB.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"}) + r.NoError(err) + a.True(serv.Whoami().Equal(&srvWho.ID)) + + // let B listen for changes + newRoomMember, err := edpOfB.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "endpoints"}) + r.NoError(err) + + newMemberChan := make(chan string) + + // read all the messages from endpoints and throw them over the channel + go func() { + for newRoomMember.Next(ctx) { + + body, err := newRoomMember.Bytes() + if err != nil { + panic(err) + } + + newMemberChan <- string(body) + } + close(newMemberChan) + }() + + // announce A + var ret struct { + Action string + Success bool + Members uint + } + err = edpOfA.Async(ctx, &ret, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "announce"}) + r.NoError(err) + a.Equal("joined", ret.Action) + a.True(ret.Success) + a.EqualValues(1, ret.Members, "expected just one member") + + select { + case <-time.After(10 * time.Second): + t.Error("timeout") + case got := <-newMemberChan: + t.Log("received join?") + t.Log(got) + } + time.Sleep(5 * time.Second) + + err = edpOfA.Async(ctx, &ret, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "leave"}) + r.NoError(err) + a.Equal("left", ret.Action) + a.True(ret.Success) + a.EqualValues(0, ret.Members, "expected empty rooms") + + select { + case <-time.After(10 * time.Second): + t.Error("timeout") + case got := <-newMemberChan: + t.Log("received leave?") + t.Log(got) + } + + // cleanup + cancel() } diff --git a/test/go/utils_test.go b/test/go/utils_test.go index 98cf299..1e359db 100644 --- a/test/go/utils_test.go +++ b/test/go/utils_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "io" + "net" "os" "path/filepath" "sync" @@ -12,6 +13,7 @@ import ( "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "github.com/stretchr/testify/require" + "go.cryptoscope.co/muxrpc/v2/debug" "go.mindeco.de/ssb-rooms/internal/maybemod/testutils" "go.mindeco.de/ssb-rooms/internal/network" @@ -59,12 +61,16 @@ func (bs botServer) Serve(s *roomsrv.Server) func() error { func makeNamedTestBot(t testing.TB, name string, opts []roomsrv.Option) *roomsrv.Server { r := require.New(t) testPath := filepath.Join("testrun", t.Name(), "bot-"+name) + os.RemoveAll(testPath) botOptions := append(opts, roomsrv.WithLogger(log.With(mainLog, "bot", name)), roomsrv.WithRepoPath(testPath), roomsrv.WithListenAddr(":0"), roomsrv.WithNetworkConnTracker(network.NewLastWinsTracker()), + roomsrv.WithPostSecureConnWrapper(func(conn net.Conn) (net.Conn, error) { + return debug.WrapDump(filepath.Join(testPath, "muxdump"), conn) + }), ) theBot, err := roomsrv.New(botOptions...) diff --git a/test/nodejs/.gitignore b/test/nodejs/.gitignore new file mode 100644 index 0000000..d1901a0 --- /dev/null +++ b/test/nodejs/.gitignore @@ -0,0 +1,2 @@ +node_modules +testrun diff --git a/test/nodejs/announce_test.go b/test/nodejs/announce_test.go new file mode 100644 index 0000000..c5abd91 --- /dev/null +++ b/test/nodejs/announce_test.go @@ -0,0 +1,150 @@ +package nodejs_test + +import ( + "context" + "net" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.cryptoscope.co/muxrpc/v2" + "go.cryptoscope.co/netwrap" + "go.cryptoscope.co/secretstream" +) + +func TestJSClient(t *testing.T) { + // defer leakcheck.Check(t) + // r := require.New(t) + + // ts := newRandomSession(t) + ts := newSession(t, nil, nil) + + ts.startGoServer() + s := ts.gobot + + alice := ts.startJSBot(` + sbot.on('rpc:connect', rpc => { + var ret = rpc.tunnel.announce() + t.comment('announced') + console.warn(ret) + pull( + rpc.tunnel.endpoints(), + pull.drain(el => { + console.warn("from roomsrv:",el) + }) + ) + + setTimeout(() => { + ret = rpc.tunnel.leave() + t.comment('left') + console.warn(ret) + }, 2500) + + setTimeout(() => { + t.comment('shutting down') + exit() + }, 5000) + }) + run()`, ``) + + s.Allow(alice, true) + + time.Sleep(5 * time.Second) + + ts.wait() + + // TODO: check wantManager for this connection is stopped when the jsbot exited + +} + +func TestJSServer(t *testing.T) { + // defer leakcheck.Check(t) + r := require.New(t) + a := assert.New(t) + + os.RemoveAll("testrun") + + // ts := newRandomSession(t) + ts := newSession(t, nil, nil) + + ts.startGoServer() + client := ts.gobot + + // alice is the server now + alice, port := ts.startJSBotAsServer("alice", "./testscripts/server.js") + + client.Allow(*alice, true) + + wrappedAddr := netwrap.WrapAddr(&net.TCPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: port, + }, secretstream.Addr{PubKey: alice.ID}) + + ctx, connCancel := context.WithCancel(context.TODO()) + err := client.Network.Connect(ctx, wrappedAddr) + defer connCancel() + r.NoError(err, "connect #1 failed") + + // this might fail if the previous node process is still running... + // TODO: properly write cleanup + + time.Sleep(5 * time.Second) + + srvEdp, has := client.Network.GetEndpointFor(*alice) + r.True(has, "botA has no endpoint for the server") + t.Log("connected") + + // let B listen for changes + newRoomMember, err := srvEdp.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "endpoints"}) + r.NoError(err) + + newMemberChan := make(chan string) + + // read all the messages from endpoints and throw them over the channel + go func() { + for newRoomMember.Next(ctx) { + body, err := newRoomMember.Bytes() + if err != nil { + panic(err) + } + newMemberChan <- string(body) + } + close(newMemberChan) + }() + + // announce A + var ret bool + err = srvEdp.Async(ctx, &ret, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "announce"}) + r.NoError(err) + // a.Equal("joined", ret.Action) + a.False(ret, "would assume these are true but..?") + // a.EqualValues(1, ret.Members, "expected just one member") + + select { + case <-time.After(10 * time.Second): + t.Error("timeout") + case got := <-newMemberChan: + t.Log("received join?") + t.Log(got) + } + time.Sleep(5 * time.Second) + + err = srvEdp.Async(ctx, &ret, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "leave"}) + r.NoError(err) + // a.Equal("left", ret.Action) + a.False(ret, "would assume these are true but..?") + // a.EqualValues(0, ret.Members, "expected empty rooms") + + select { + case <-time.After(10 * time.Second): + t.Error("timeout") + case got := <-newMemberChan: + t.Log("received leave?") + t.Log(got) + } + + ts.wait() + +} diff --git a/test/nodejs/package-lock.json b/test/nodejs/package-lock.json new file mode 100644 index 0000000..3df8795 --- /dev/null +++ b/test/nodejs/package-lock.json @@ -0,0 +1,2671 @@ +{ + "name": "go-ssb-rooms-tests", + "version": "1.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abstract-leveldown": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", + "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", + "requires": { + "buffer": "^5.5.0", + "immediate": "^3.2.3", + "level-concat-iterator": "~2.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "aligned-block-file": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/aligned-block-file/-/aligned-block-file-1.2.2.tgz", + "integrity": "sha512-2Sy0hWhifVb8ycNFJgicL8fDPL2Ct1r62XOVxXnykn36z22MPZwnQlCmB2viQlY/lwfuO67GaQjUZ0rJgdVP7Q==", + "requires": { + "hashlru": "^2.1.0", + "int53": "^1.0.0", + "mkdirp": "^0.5.1", + "obv": "^0.0.1", + "rwlock": "^5.0.0", + "uint48be": "^2.0.1" + } + }, + "append-batch": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/append-batch/-/append-batch-0.0.2.tgz", + "integrity": "sha1-1zm0UDiIJF1Hkz1HVisRSf+d+Lc=" + }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "async-single": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/async-single/-/async-single-1.0.5.tgz", + "integrity": "sha1-El3QneldPqMKN4rb7QIQkhebA8k=" + }, + "atomic-file": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atomic-file/-/atomic-file-2.1.1.tgz", + "integrity": "sha512-Eh6pW+fRC2/1RxPq3hO8+PkZKv+wujzKky2MP/n69eC8yMkbNFfuEb/riZHqf13M7gr6Hvglpk/kISgBSBb6bQ==", + "requires": { + "flumecodec": "0.0.1", + "idb-kv-store": "^4.5.0", + "mutexify": "^1.2.0" + }, + "dependencies": { + "flumecodec": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/flumecodec/-/flumecodec-0.0.1.tgz", + "integrity": "sha1-rgSacUOGu4PjQmV6gpJLcDZKkNY=", + "requires": { + "level-codec": "^6.2.0" + } + }, + "level-codec": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-6.2.0.tgz", + "integrity": "sha1-pLUkS7akwvcj1oodZOmAxTYn2dQ=" + } + } + }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "requires": { + "array-filter": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bash-color": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/bash-color/-/bash-color-0.0.4.tgz", + "integrity": "sha1-6b6M4zVAytpIgXaMWb1jhlc26RM=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "charwise": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/charwise/-/charwise-3.0.1.tgz", + "integrity": "sha512-RcdumNsM6fJZ5HHbYunqj2bpurVRGsXour3OR+SlLEHFhG6ALm54i6Osnh+OvO7kEoSBzwExpblYFH8zKQiEPw==" + }, + "chloride": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/chloride/-/chloride-2.4.0.tgz", + "integrity": "sha512-Fgz7EaWswRjRBeft6pCkg8STSQTQT/5GrRZlbtlx5hkhFi/X1RSIhSustfe2GsbySVZF2DuxsWri8GDFJ/SkvQ==", + "requires": { + "sodium-browserify": "^1.2.7", + "sodium-browserify-tweetnacl": "^0.2.5", + "sodium-chloride": "^1.1.2", + "sodium-native": "^3.0.0" + } + }, + "chloride-test": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/chloride-test/-/chloride-test-1.2.4.tgz", + "integrity": "sha512-9vhoi1qXSBPn6//ZxIgSe3M2QhKHzIPZQzmrZgmPADsqW0Jxpe3db1e7aGSRUMXbxAQ04SfypdT8dGaSvIvKDw==", + "requires": { + "json-buffer": "^2.0.11" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "cont": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cont/-/cont-1.0.3.tgz", + "integrity": "sha1-aHTx6TX8qZ0EjK6qrZoK6wILzOA=", + "requires": { + "continuable": "~1.2.0", + "continuable-para": "~1.2.0", + "continuable-series": "~1.2.0" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "continuable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/continuable/-/continuable-1.2.0.tgz", + "integrity": "sha1-CCd0aNQRNiAAdMz4cpQwjRafJbY=" + }, + "continuable-hash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/continuable-hash/-/continuable-hash-0.1.4.tgz", + "integrity": "sha1-gcdNQXcdjJJ4Ph4A5fEbNNbfx4w=", + "requires": { + "continuable": "~1.1.6" + }, + "dependencies": { + "continuable": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/continuable/-/continuable-1.1.8.tgz", + "integrity": "sha1-3Id7R0FghwrjvN6HM2Jo6+UFl9U=" + } + } + }, + "continuable-list": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/continuable-list/-/continuable-list-0.1.6.tgz", + "integrity": "sha1-h88G7FgHFuEN/5X7C4TF8OisrF8=", + "requires": { + "continuable": "~1.1.6" + }, + "dependencies": { + "continuable": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/continuable/-/continuable-1.1.8.tgz", + "integrity": "sha1-3Id7R0FghwrjvN6HM2Jo6+UFl9U=" + } + } + }, + "continuable-para": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/continuable-para/-/continuable-para-1.2.0.tgz", + "integrity": "sha1-RFUQ9klFndD8NchyAVFGEicxxYM=", + "requires": { + "continuable-hash": "~0.1.4", + "continuable-list": "~0.1.5" + } + }, + "continuable-series": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/continuable-series/-/continuable-series-1.2.0.tgz", + "integrity": "sha1-MkM5euk6cdZVswJoNKUVkLlYueg=" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deferred-leveldown": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", + "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", + "requires": { + "abstract-leveldown": "~6.2.1", + "inherits": "^2.0.3" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=" + }, + "dotignore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", + "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "ed2curve": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.1.4.tgz", + "integrity": "sha1-lKRCSLuH2jXbDv968KpXYWgRf1k=", + "requires": { + "tweetnacl": "0.x.x" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding-down": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", + "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", + "requires": { + "abstract-leveldown": "^6.2.1", + "inherits": "^2.0.3", + "level-codec": "^9.0.0", + "level-errors": "^2.0.0" + } + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, + "es-abstract": { + "version": "1.18.0-next.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", + "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.1", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.3", + "string.prototype.trimstart": "^1.0.3" + } + }, + "es-get-iterator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.1.tgz", + "integrity": "sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.1", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "explain-error": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/explain-error/-/explain-error-1.0.4.tgz", + "integrity": "sha1-p5PTrAytTGq1cemWj7urbLJTKSk=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "flumecodec": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/flumecodec/-/flumecodec-0.0.0.tgz", + "integrity": "sha1-Ns4Gq+Lg4BxE3WnyoWUwWiMgZJs=", + "requires": { + "level-codec": "^6.2.0" + }, + "dependencies": { + "level-codec": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-6.2.0.tgz", + "integrity": "sha1-pLUkS7akwvcj1oodZOmAxTYn2dQ=" + } + } + }, + "flumedb": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/flumedb/-/flumedb-2.1.8.tgz", + "integrity": "sha512-MtBCZFjj9GuqOQP8Ld87FbXm8ztQyLkLeuiHuB5+aACFuVn1kunnCis75R03ujFZTqCFmkBwFz7E016b3DB0zA==", + "requires": { + "cont": "^1.0.3", + "explain-error": "^1.0.3", + "obz": "1.0.2", + "pull-abortable": "^4.1.1", + "pull-cont": "^0.1.1", + "pull-looper": "^1.0.0", + "pull-stream": "^3.6.14" + }, + "dependencies": { + "pull-abortable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/pull-abortable/-/pull-abortable-4.1.1.tgz", + "integrity": "sha1-s61a77QRayWRbSbbiTk6yY0NzqE=" + } + } + }, + "flumelog-offset": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flumelog-offset/-/flumelog-offset-3.4.4.tgz", + "integrity": "sha512-sakCD+dVx541h3VeVq3Ti2lWPRrJf8PBRmnbm9EMBVLJnZkS3UD2lAlClZROxgKbh/JkMPyffvhDGv4VHNCVbA==", + "requires": { + "aligned-block-file": "^1.2.0", + "append-batch": "0.0.2", + "hashlru": "^2.3.0", + "int53": "^1.0.0", + "looper": "^4.0.0", + "obv": "0.0.1", + "pull-cursor": "^3.0.0", + "pull-looper": "^1.0.0", + "pull-stream": "^3.6.13", + "uint48be": "^2.0.1" + }, + "dependencies": { + "looper": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-4.0.0.tgz", + "integrity": "sha1-dwat7VmpntygbmtUu4bI7BnJUVU=" + } + } + }, + "flumeview-level": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flumeview-level/-/flumeview-level-4.0.4.tgz", + "integrity": "sha512-8C/o/oZU73ot1LMbxCyKeZJ0D3L5AGdxzIF5H2QtmznMSoZHVG1gT2IDjkOtesenVPlLQKnL95ewMKbE7cXWEw==", + "requires": { + "charwise": "^3.0.1", + "explain-error": "^1.0.4", + "level": "^6.0.1", + "ltgt": "^2.1.3", + "mkdirp": "^1.0.4", + "obz": "^1.0.2", + "pull-level": "^2.0.3", + "pull-paramap": "^1.2.1", + "pull-stream": "^3.6.14", + "pull-write": "^1.1.1" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "flumeview-reduce": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/flumeview-reduce/-/flumeview-reduce-1.4.0.tgz", + "integrity": "sha512-ShCMtY5YZl3icnUAWTfwzRXSGN1AUo4Isj1wTRLpMMNgM9p1T0gc6N+r1ajampf1GO19SkqrgZHdPPOaEitvhg==", + "requires": { + "async-single": "^1.0.5", + "atomic-file": "^2.0.0", + "deep-equal": "^1.0.1", + "flumecodec": "0.0.0", + "obv": "0.0.1", + "pull-notify": "^0.1.1", + "pull-stream": "^3.5.0" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.0.tgz", + "integrity": "sha512-M11rgtQp5GZMZzDL7jLTNxbDfurpzuau5uqRWDPvlHjfvg3TdScAZo96GLvhMjImrmR8uAt0FS2RLoMrfWGKlg==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-network": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/has-network/-/has-network-0.0.1.tgz", + "integrity": "sha1-Pup7RMqpYBeXEkvouonSKMQQFJk=" + }, + "has-network2": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/has-network2/-/has-network2-0.0.3.tgz", + "integrity": "sha512-EvEZguA+LkyiS8G/Qks5I6imKnM2Z3NPN3eoQhviUQ7O6/d8nyZ7sDozBk6kTIA+Qj/S/V8ubRA1rqJcxc3qBQ==" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "hashlru": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz", + "integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==" + }, + "hoox": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/hoox/-/hoox-0.0.1.tgz", + "integrity": "sha1-CKdNknKpzIOujmu+AwPw7nZDIJQ=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idb-kv-store": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/idb-kv-store/-/idb-kv-store-4.5.0.tgz", + "integrity": "sha512-snvtAQRforYUI+C2+45L2LBJy/0/uQUffxv8/uwiS98fSUoXHVrFPClgzWZWxT0drwkLHJRm9inZcYzTR42GLA==", + "requires": { + "inherits": "^2.0.3", + "promisize": "^1.1.2" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, + "increment-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/increment-buffer/-/increment-buffer-1.0.1.tgz", + "integrity": "sha1-ZQdtdRidgIs5rROrW5WOBSFvng0=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "int53": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/int53/-/int53-1.0.0.tgz", + "integrity": "sha512-u8BMiMa05OPBgd32CKTead0CVTsFVgwFk23nNXo1teKPF6Sxcu0lXxEzP//zTcaKzXbGgPDXGmj/woyv+I4C5w==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-bigint": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", + "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==" + }, + "is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-canonical-base64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-canonical-base64/-/is-canonical-base64-1.1.1.tgz", + "integrity": "sha512-o6t/DwgEapC0bsloqtegAQyZzQXaQ5+8fzsyf2KmLqupC2ifLFq/lMQiFCJeGpdSrK1o6GL+WW2lRU050lLlFg==" + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz", + "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, + "is-valid-domain": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/is-valid-domain/-/is-valid-domain-0.0.17.tgz", + "integrity": "sha512-w0UWEXyrgPeWWwj9FVT14y4/dSIqWgjDkzxbsGDFpT+QRbyS9HTwwNvGus2IOR/03GzCpeChzSWK9Bo9WlStDA==" + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==" + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "json-buffer": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-2.0.11.tgz", + "integrity": "sha1-PkQf2jCYvo0eMXGtWRvGKjPi1V8=" + }, + "level": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz", + "integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==", + "requires": { + "level-js": "^5.0.0", + "level-packager": "^5.1.0", + "leveldown": "^5.4.0" + } + }, + "level-codec": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", + "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", + "requires": { + "buffer": "^5.6.0" + } + }, + "level-concat-iterator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", + "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==" + }, + "level-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", + "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", + "requires": { + "errno": "~0.1.1" + } + }, + "level-iterator-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", + "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.4.0", + "xtend": "^4.0.2" + } + }, + "level-js": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz", + "integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==", + "requires": { + "abstract-leveldown": "~6.2.3", + "buffer": "^5.5.0", + "inherits": "^2.0.3", + "ltgt": "^2.1.2" + } + }, + "level-packager": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", + "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", + "requires": { + "encoding-down": "^6.3.0", + "levelup": "^4.3.2" + } + }, + "level-post": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/level-post/-/level-post-1.0.7.tgz", + "integrity": "sha512-PWYqG4Q00asOrLhX7BejSajByB4EmG2GaKHfj3h5UmmZ2duciXLPGYWIjBzLECFWUGOZWlm5B20h/n3Gs3HKew==", + "requires": { + "ltgt": "^2.1.2" + } + }, + "level-supports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", + "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", + "requires": { + "xtend": "^4.0.2" + } + }, + "leveldown": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz", + "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==", + "requires": { + "abstract-leveldown": "~6.2.1", + "napi-macros": "~2.0.0", + "node-gyp-build": "~4.1.0" + }, + "dependencies": { + "node-gyp-build": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", + "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==" + } + } + }, + "levelup": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", + "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", + "requires": { + "deferred-leveldown": "~5.3.0", + "level-errors": "~2.0.0", + "level-iterator-stream": "~4.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + } + }, + "libsodium": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.8.tgz", + "integrity": "sha512-/Qc+APf0jbeWSaeEruH0L1/tbbT+sbf884ZL0/zV/0JXaDPBzYkKbyb/wmxMHgAHzm3t6gqe7bOOXAVwfqVikQ==" + }, + "libsodium-wrappers": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.8.tgz", + "integrity": "sha512-PDhPWXBqd/SaqAFUBgH2Ux7b3VEEJgyD6BQB+VdNFJb9PbExGr/T/myc/MBoSvl8qLzfm0W0IVByOQS5L1MrCg==", + "requires": { + "libsodium": "0.7.8" + } + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "looper": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-3.0.0.tgz", + "integrity": "sha1-LvpUw7HLq6m5Su4uWRSwvlf7t0k=" + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" + }, + "map-merge": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/map-merge/-/map-merge-1.1.0.tgz", + "integrity": "sha1-am/FjJXYqrRsK93kTVFbbuBvzjQ=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" + }, + "mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "requires": { + "mime-db": "1.45.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "monotonic-timestamp": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/monotonic-timestamp/-/monotonic-timestamp-0.0.9.tgz", + "integrity": "sha1-W6Wtx6rIXh1853voRxYe0kazlgM=" + }, + "moo": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", + "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/multicb/-/multicb-1.2.2.tgz", + "integrity": "sha512-PZM4dhYFmCF6uZGWpEmoPMUqJBywS9IcAgybT2GmSpYI1BvGvoWSdbio+ik+q/YD2vodhvslESWIS3NnkKYdqQ==" + }, + "multiserver": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/multiserver/-/multiserver-3.7.0.tgz", + "integrity": "sha512-70SSDMNT+e3VsDG4x7OKFW8+UqyZsBWfKD9rAvsRWCbMe9ySODheJCZ91Xyha5FrA32UtWIHGSY3m5jATfEmVQ==", + "requires": { + "debug": "^4.1.1", + "multicb": "^1.2.2", + "multiserver-scopes": "^1.0.0", + "pull-stream": "^3.6.1", + "pull-ws": "^3.3.0", + "secret-handshake": "^1.1.16", + "separator-escape": "0.0.1", + "socks": "^2.2.3", + "stream-to-pull-stream": "^1.7.2" + } + }, + "multiserver-address": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/multiserver-address/-/multiserver-address-1.0.1.tgz", + "integrity": "sha512-IfZMAGs9onCLkYNSnNBri3JxuvhQYllMyh3W9ry86iEDcfW9uPVsHTHDsjDxQtL+dPq3byshmA+Y4LN2wLHwNw==", + "requires": { + "nearley": "^2.15.1" + } + }, + "multiserver-scopes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/multiserver-scopes/-/multiserver-scopes-1.0.0.tgz", + "integrity": "sha512-D3q4IujGRUIKETfR5s0kRtvXTjAMhyl7rtLEMXtvkg0lJPJyS5KYsAULFFy+dYv/+RC642aR1zo/RKNp6sdtQg==", + "requires": { + "non-private-ip": "^1.4.4" + } + }, + "mutexify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.3.1.tgz", + "integrity": "sha512-nU7mOEuaXiQIB/EgTIjYZJ7g8KqMm2D8l4qp+DqA4jxWOb/tnb1KEoqp+tlbdQIDIAiC1i7j7X/3yHDFXLxr9g==" + }, + "muxrpc": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/muxrpc/-/muxrpc-6.5.2.tgz", + "integrity": "sha512-fgYhBfzevyUbwsB8YBlrnmzZOGxWv6OiAUNKQYwPLqbophsZ+GT8STKrCVHCYNjUx6btxFA5+BJPUCFMecyaSA==", + "requires": { + "explain-error": "^1.0.1", + "packet-stream": "~2.0.0", + "packet-stream-codec": "^1.1.3", + "pull-goodbye": "0.0.2", + "pull-stream": "^3.6.10" + } + }, + "muxrpc-validation": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/muxrpc-validation/-/muxrpc-validation-3.0.2.tgz", + "integrity": "sha512-iWo/23xFnl+IGeX+LlfwoVKtyY4volPSodf3nwPScPgxjws4k2ZUozPG98OouMA0yn0JamqApjRw7eqLrzyV2A==", + "requires": { + "pull-stream": "^3.6.11", + "zerr": "^1.0.4" + } + }, + "napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==" + }, + "nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "requires": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-gyp-build": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", + "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" + }, + "non-private-ip": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/non-private-ip/-/non-private-ip-1.4.4.tgz", + "integrity": "sha512-K9nTVFOGUOYutaG8ywiKpCdVu458RFxSgSJ0rribUxtf5iLM9B2+raFJgkID3p5op0+twmoQqFaPnu9KYz6qzg==", + "requires": { + "ip": "^1.1.5" + } + }, + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" + }, + "object-is": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", + "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "observ": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/observ/-/observ-0.2.0.tgz", + "integrity": "sha1-C8ObPin6pfnmyqWQbLg5LfQAqmg=" + }, + "observ-debounce": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/observ-debounce/-/observ-debounce-1.1.1.tgz", + "integrity": "sha1-ME6XyFrdpw7NfwjaRQZ475Dwtwc=", + "requires": { + "observ": "~0.2.0" + } + }, + "obv": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/obv/-/obv-0.0.1.tgz", + "integrity": "sha1-yyNhBjQVNvDaxIFeBnCCIcrX+14=" + }, + "obz": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/obz/-/obz-1.0.2.tgz", + "integrity": "sha512-c+EtVwT2IpXz5we2mR40aPLJ1s0eNOsxYeaYbaHhmsY6kWKo3IRkpwpBU5ck0aHfqfKUUEiKabC6rzsrG/hSHw==" + }, + "on-change-network": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/on-change-network/-/on-change-network-0.0.2.tgz", + "integrity": "sha1-2XcklHf5FyaUnYDoI0batu9FIWs=" + }, + "on-change-network-strict": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/on-change-network-strict/-/on-change-network-strict-1.0.0.tgz", + "integrity": "sha512-ldHCpTJWgr5KUJy3/TVoSGNwBUA8BP9UFmd0iQqe4aGaXY4PJyzQPiVBIo8VBSlSoKyaJY3vcpW0hixZb6gPaA==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-wakeup": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-wakeup/-/on-wakeup-1.0.1.tgz", + "integrity": "sha1-ANedmH3efIEXvudLtJA/b22vpSs=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "options": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "packet-stream": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/packet-stream/-/packet-stream-2.0.5.tgz", + "integrity": "sha512-+4S+qBUdqD57ka5MDd6nAYGBPril5eyLpbga2y0kPyYhrKvjb8CYTP9r40WLbSxgT/qEGmvgWOrvQe+FYtCI7w==" + }, + "packet-stream-codec": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/packet-stream-codec/-/packet-stream-codec-1.1.3.tgz", + "integrity": "sha512-LUL4NK7sz01jdSUdCu3z1LyphCiFdQaFouaEDsAWmJpzS0lbeNfvZoX4bi1Tm1ilzheK5VAoD96QskDCZQr+jA==", + "requires": { + "pull-reader": "^1.2.4", + "pull-through": "^1.0.17" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "private-box": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/private-box/-/private-box-0.3.1.tgz", + "integrity": "sha512-abAuk3ZDyQvPLY6MygtwaDTUBIZ0C5wMMuX1jXa0svazV+keTwn7cPobRv4WYA9ctsDUztm/9CYu4y2TPL08xw==", + "requires": { + "chloride": "^2.2.9" + } + }, + "promisify-tuple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promisify-tuple/-/promisify-tuple-1.0.1.tgz", + "integrity": "sha512-st4Q1R6oAr8hzt1hj3uCYv87Pc5wtDkoq7ZqxWri2xR3x5Zvx7syH2DR4fgknVhMsZ6GV+kxEmhn2tVnwFMmJw==" + }, + "promisize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/promisize/-/promisize-1.1.2.tgz", + "integrity": "sha1-m0fiyyrkl+seutwsQZHWTRXJSdE=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pull-abortable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pull-abortable/-/pull-abortable-4.0.0.tgz", + "integrity": "sha1-cBephMO4NN53usOMELd28i38GEM=" + }, + "pull-box-stream": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/pull-box-stream/-/pull-box-stream-1.0.13.tgz", + "integrity": "sha1-w+JAOY6rP1lRsu0QeMWYi/egork=", + "requires": { + "chloride": "^2.2.7", + "increment-buffer": "~1.0.0", + "pull-reader": "^1.2.5", + "pull-stream": "^3.2.3", + "pull-through": "^1.0.18", + "split-buffer": "~1.0.0" + } + }, + "pull-cat": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/pull-cat/-/pull-cat-1.1.11.tgz", + "integrity": "sha1-tkLdElXaN2pwa220+pYvX9t0wxs=" + }, + "pull-cont": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pull-cont/-/pull-cont-0.1.1.tgz", + "integrity": "sha1-3x1YDicXV7qay666IN4kIdZg1hg=" + }, + "pull-cursor": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pull-cursor/-/pull-cursor-3.0.0.tgz", + "integrity": "sha512-95lZVSF2eSEdOmUtlOBaD9p5YOvlYeCr5FBv2ySqcj/4rpaXI6d8OH+zPHHjKAf58R8QXJRZuyfHkcCX8TZbAg==", + "requires": { + "looper": "^4.0.0", + "ltgt": "^2.2.0", + "pull-stream": "^3.6.0" + }, + "dependencies": { + "looper": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-4.0.0.tgz", + "integrity": "sha1-dwat7VmpntygbmtUu4bI7BnJUVU=" + } + } + }, + "pull-goodbye": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/pull-goodbye/-/pull-goodbye-0.0.2.tgz", + "integrity": "sha1-jYNX21XiKnEN//DxaoyQtF7+QXE=", + "requires": { + "pull-stream": "~3.5.0" + }, + "dependencies": { + "pull-stream": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/pull-stream/-/pull-stream-3.5.0.tgz", + "integrity": "sha1-HuW292/Ts6SaWvtt7VwDIKyzz8c=" + } + } + }, + "pull-handshake": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pull-handshake/-/pull-handshake-1.1.4.tgz", + "integrity": "sha1-YACg/QGIhM39c3JU+Mxgqypjd5E=", + "requires": { + "pull-cat": "^1.1.9", + "pull-pair": "~1.1.0", + "pull-pushable": "^2.0.0", + "pull-reader": "^1.2.3" + } + }, + "pull-hash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pull-hash/-/pull-hash-1.0.0.tgz", + "integrity": "sha1-/K1NJQe/LCsyMfZT3Jv7LbTw2Iw=" + }, + "pull-inactivity": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/pull-inactivity/-/pull-inactivity-2.1.3.tgz", + "integrity": "sha512-swJ/jwkIN/O1bQCE3iY7Xy9r3gYuJ50MXaxZilw/HIduAy4tJu+vcz2/If0L+xNK7Ku/FfjtVbTpRTe7sf3hmA==", + "requires": { + "pull-abortable": "~4.0.0", + "pull-stream": "^3.4.5" + } + }, + "pull-level": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pull-level/-/pull-level-2.0.4.tgz", + "integrity": "sha512-fW6pljDeUThpq5KXwKbRG3X7Ogk3vc75d5OQU/TvXXui65ykm+Bn+fiktg+MOx2jJ85cd+sheufPL+rw9QSVZg==", + "requires": { + "level-post": "^1.0.7", + "pull-cat": "^1.1.9", + "pull-live": "^1.0.1", + "pull-pushable": "^2.0.0", + "pull-stream": "^3.4.0", + "pull-window": "^2.1.4", + "stream-to-pull-stream": "^1.7.1" + } + }, + "pull-live": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pull-live/-/pull-live-1.0.1.tgz", + "integrity": "sha1-pOzuAeMwFV6RJLu89HYfIbOPUfU=", + "requires": { + "pull-cat": "^1.1.9", + "pull-stream": "^3.4.0" + } + }, + "pull-looper": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pull-looper/-/pull-looper-1.0.0.tgz", + "integrity": "sha512-djlD60A6NGe5goLdP5pgbqzMEiWmk1bInuAzBp0QOH4vDrVwh05YDz6UP8+pOXveKEk8wHVP+rB2jBrK31QMPA==", + "requires": { + "looper": "^4.0.0" + }, + "dependencies": { + "looper": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-4.0.0.tgz", + "integrity": "sha1-dwat7VmpntygbmtUu4bI7BnJUVU=" + } + } + }, + "pull-next": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pull-next/-/pull-next-1.0.1.tgz", + "integrity": "sha1-A/TX0Zhy/BEUFh6I227PTGXmHlY=" + }, + "pull-notify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pull-notify/-/pull-notify-0.1.1.tgz", + "integrity": "sha1-b4b/ldJwuJw+vyVbYDG3Ay3JnMo=", + "requires": { + "pull-pushable": "^2.0.0" + } + }, + "pull-pair": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pull-pair/-/pull-pair-1.1.0.tgz", + "integrity": "sha1-fuQnJj/fTaglOXrAoF4atLdL120=" + }, + "pull-paramap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/pull-paramap/-/pull-paramap-1.2.2.tgz", + "integrity": "sha1-UaQZPOnI1yFdla2tReK824STsjo=", + "requires": { + "looper": "^4.0.0" + }, + "dependencies": { + "looper": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-4.0.0.tgz", + "integrity": "sha1-dwat7VmpntygbmtUu4bI7BnJUVU=" + } + } + }, + "pull-pause": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/pull-pause/-/pull-pause-0.0.2.tgz", + "integrity": "sha1-GdRb6PqmFfpVbxSpb9czRiw3+6M=" + }, + "pull-ping": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pull-ping/-/pull-ping-2.0.3.tgz", + "integrity": "sha512-nbY4yHnMesJBrvkbhMim4VXUC9k1VCkgrkQu49pf8mxFbmb/U2KQrsuePvSmLjRL+VgkBVRSUXUoOY7DtSvhKw==", + "requires": { + "pull-pushable": "^2.0.0", + "pull-stream": "^3.4.5", + "statistics": "^3.3.0" + } + }, + "pull-pushable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pull-pushable/-/pull-pushable-2.2.0.tgz", + "integrity": "sha1-Xy867UethpGfAbEqLpnW8b13ZYE=" + }, + "pull-rate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pull-rate/-/pull-rate-1.0.2.tgz", + "integrity": "sha1-F7IxrV81n2dYJmcBcrDlkMiWTo0=", + "requires": { + "pull-stream": "^3.6.0" + } + }, + "pull-reader": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pull-reader/-/pull-reader-1.3.1.tgz", + "integrity": "sha512-CBkejkE5nX50SiSEzu0Qoz4POTJMS/mw8G6aj3h3M/RJoKgggLxyF0IyTZ0mmpXFlXRcLmLmIEW4xeYn7AeDYw==" + }, + "pull-stream": { + "version": "3.6.14", + "resolved": "https://registry.npmjs.org/pull-stream/-/pull-stream-3.6.14.tgz", + "integrity": "sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew==" + }, + "pull-through": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/pull-through/-/pull-through-1.0.18.tgz", + "integrity": "sha1-jdYjFCY+Wc9Qlur7sSeitu8xBzU=", + "requires": { + "looper": "~3.0.0" + } + }, + "pull-window": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/pull-window/-/pull-window-2.1.4.tgz", + "integrity": "sha1-/DuG/uvRkgx64pdpHiP3BfiFUvA=", + "requires": { + "looper": "^2.0.0" + }, + "dependencies": { + "looper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-2.0.0.tgz", + "integrity": "sha1-Zs0Md0rz1P7axTeU90LbVtqPCew=" + } + } + }, + "pull-write": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pull-write/-/pull-write-1.1.4.tgz", + "integrity": "sha1-3d6jFJO0j2douEooHQHrO1Mf4Lg=", + "requires": { + "looper": "^4.0.0", + "pull-cat": "^1.1.11", + "pull-stream": "^3.4.5" + }, + "dependencies": { + "looper": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-4.0.0.tgz", + "integrity": "sha1-dwat7VmpntygbmtUu4bI7BnJUVU=" + } + } + }, + "pull-ws": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/pull-ws/-/pull-ws-3.3.2.tgz", + "integrity": "sha512-Bn4bcJsSzJGOQl4RBulDhG1FkcbDHSCXteI8Jg5k4X6X5TxVzZzKilWJ1WV2v4OnRXl2eYbtHFGsPl8Cr1xJzw==", + "requires": { + "relative-url": "^1.0.2", + "safe-buffer": "^5.1.1", + "ws": "^1.1.0" + } + }, + "qr-image": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/qr-image/-/qr-image-3.2.0.tgz", + "integrity": "sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=" + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "relative-url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/relative-url/-/relative-url-1.0.2.tgz", + "integrity": "sha1-0hxSpy1gYQGLzun5yfwQa/fWUoc=" + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "requires": { + "through": "~2.3.4" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", + "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==" + }, + "run-series": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz", + "integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==" + }, + "rwlock": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/rwlock/-/rwlock-5.0.0.tgz", + "integrity": "sha1-iI1qd6M1HMGiCSBO8u4XIgk4Ns8=" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "secret-handshake": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/secret-handshake/-/secret-handshake-1.1.20.tgz", + "integrity": "sha512-sDtmZDpibGH2ixj3FOmsC3Z/b08eaB2/KAvy2oSp4qvcGdhatBSfb1RdVpwjQl5c3J83WbBo1HSZ7DBtMu43lA==", + "requires": { + "chloride": "^2.2.8", + "explain-error": "^1.0.4", + "pull-box-stream": "^1.0.13", + "pull-handshake": "^1.1.1", + "pull-stream": "^3.4.5" + } + }, + "secret-stack": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/secret-stack/-/secret-stack-6.3.2.tgz", + "integrity": "sha512-D46+4LWwsM1LnO4dg6FM/MfGmMk9uYsIcDElqyNeImBnyUueKi2xz10CHF9iSAtSUGReQDV4SCVUiVrPnaKnsA==", + "requires": { + "debug": "^4.1.0", + "hoox": "0.0.1", + "map-merge": "^1.1.0", + "multiserver": "^3.1.0", + "muxrpc": "^6.5.2", + "pull-inactivity": "~2.1.1", + "pull-rate": "^1.0.2", + "pull-stream": "^3.4.5", + "to-camel-case": "^1.0.0" + } + }, + "secret-stack-decorators": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/secret-stack-decorators/-/secret-stack-decorators-1.1.0.tgz", + "integrity": "sha512-wYl0Mcul/fuEbZwn9tN62c+W4LP2RPus/ilt3wdBNQqfBFSMlDXTLaIsrA4SEElb7JEBH4xzdQbOCnTvYHeWCA==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "separator-escape": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/separator-escape/-/separator-escape-0.0.1.tgz", + "integrity": "sha512-daCzTzZVoowYzjW7x9xMH6zr+lt/zsGxV1rtXaoTnlues7ZDx6Qu0l5W3jCdgnXGE1ONAGL+XPWY+IRDxnJ9EQ==" + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.5.tgz", + "integrity": "sha1-J9Fx78yCoRi5ljn/WBZgJCtQbnw=", + "requires": { + "inherits": "^2.0.1" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" + }, + "socks": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.5.1.tgz", + "integrity": "sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "sodium-browserify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sodium-browserify/-/sodium-browserify-1.3.0.tgz", + "integrity": "sha512-1KRS6Oew3X13AIZhbmGF0YBdt2pQdafJMfv83OZHWbzxG92YBBnN8HYx/VKmYB4xCe90eidNaDJWBEFw/o3ahw==", + "requires": { + "libsodium-wrappers": "^0.7.4", + "sha.js": "2.4.5", + "sodium-browserify-tweetnacl": "^0.2.5", + "tweetnacl": "^0.14.1" + } + }, + "sodium-browserify-tweetnacl": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/sodium-browserify-tweetnacl/-/sodium-browserify-tweetnacl-0.2.6.tgz", + "integrity": "sha512-ZnEI26hdluilpYY28Xc4rc1ALfmEp2TWihkJX6Mdtw0z9RfHfpZJU7P8DoKbN1HcBdU9aJmguFZs7igE8nLJPg==", + "requires": { + "chloride-test": "^1.1.0", + "ed2curve": "^0.1.4", + "sha.js": "^2.4.8", + "tweetnacl": "^1.0.1", + "tweetnacl-auth": "^0.3.0" + }, + "dependencies": { + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + } + } + }, + "sodium-chloride": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sodium-chloride/-/sodium-chloride-1.1.2.tgz", + "integrity": "sha512-8AVzr9VHueXqfzfkzUA0aXe/Q4XG3UTmhlP6Pt+HQc5bbAPIJFo7ZIMh9tvn+99QuiMcyDJdYumegGAczl0N+g==" + }, + "sodium-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-3.2.0.tgz", + "integrity": "sha512-8aq/vQSegLwsRch8Sb/Bpf9aAqlNe5dp0+NVhb9UjHv42zDZ0D5zX3wBRUbXK9Ejum9uZE6DUgT4vVLlUFRBWg==", + "requires": { + "ini": "^1.3.5", + "node-gyp-build": "^4.2.0" + } + }, + "split-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split-buffer/-/split-buffer-1.0.0.tgz", + "integrity": "sha1-t+jgq1E0UVi3LB9tvvJAbVHx0Cc=" + }, + "ssb-caps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ssb-caps/-/ssb-caps-1.1.0.tgz", + "integrity": "sha512-qe3qpvchJ+gnH8M/ge4rpL+7eRbSmsEAzNwHkDdrW06OBcziQ6/KuAdmcR6joxCbNeoAXAZF+inkefgE16okXA==" + }, + "ssb-client": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/ssb-client/-/ssb-client-4.7.9.tgz", + "integrity": "sha512-koVuazgGa+Pz7KHnPTsOFvaKSrE0T38wSO7n6P7ZQp+1BpaoBCI4z8Kq2SvMw521Sz9dK9Rwh2bTakEDRgx84A==", + "requires": { + "explain-error": "^1.0.1", + "multicb": "^1.2.1", + "multiserver": "^3.1.2", + "muxrpc": "^6.4.8", + "pull-hash": "^1.0.0", + "pull-stream": "^3.6.0", + "ssb-config": "^3.2.5", + "ssb-keys": "^7.0.13" + }, + "dependencies": { + "ssb-keys": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/ssb-keys/-/ssb-keys-7.2.2.tgz", + "integrity": "sha512-FPeyYU/3LpxcagnbmVWE+Q/qzg6keqeOBPbD7sEH9UKixUASeufPKiORDgh8nVX7J9Z+0vUaHt/WG999kGjvVQ==", + "requires": { + "chloride": "^2.2.8", + "mkdirp": "~0.5.0", + "private-box": "^0.3.0" + } + } + } + }, + "ssb-config": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/ssb-config/-/ssb-config-3.4.5.tgz", + "integrity": "sha512-DyCrGIsl01GkdHreAkkaDUorV7SAgRSqKn/htg4ZwbvH6g0NAdOi84x/8ehzDuojPev78hbkWjZXgIqi+/Jo0g==", + "requires": { + "deep-extend": "^0.6.0", + "ip": "^1.1.5", + "lodash.get": "^4.4.2", + "os-homedir": "^1.0.1", + "rc": "^1.1.6", + "ssb-caps": "^1.0.1", + "ssb-keys": "^7.1.4" + }, + "dependencies": { + "ssb-keys": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/ssb-keys/-/ssb-keys-7.2.2.tgz", + "integrity": "sha512-FPeyYU/3LpxcagnbmVWE+Q/qzg6keqeOBPbD7sEH9UKixUASeufPKiORDgh8nVX7J9Z+0vUaHt/WG999kGjvVQ==", + "requires": { + "chloride": "^2.2.8", + "mkdirp": "~0.5.0", + "private-box": "^0.3.0" + } + } + } + }, + "ssb-conn": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/ssb-conn/-/ssb-conn-0.19.1.tgz", + "integrity": "sha512-7HEiZPZyeIurrZsL1kxYgM164eNU+PRbqfhMzGNwQOrU5Ku+tciZhrZsbYtgNJHHIaoQQZfX79IaQQsD1ZrW5A==", + "requires": { + "debug": "~4.2.0", + "has-network2": ">=0.0.3", + "ip": "^1.1.5", + "on-change-network-strict": "1.0.0", + "on-wakeup": "^1.0.1", + "pull-notify": "^0.1.1", + "pull-pause": "~0.0.2", + "pull-ping": "^2.0.3", + "pull-stream": "^3.6.14", + "secret-stack-decorators": "1.1.0", + "ssb-conn-db": "~0.3.3", + "ssb-conn-hub": "~0.2.7", + "ssb-conn-query": "~0.4.6", + "ssb-conn-staging": "~0.1.0", + "ssb-ref": "^2.14.2", + "ssb-typescript": "^2.1.0", + "statistics": "^3.3.0", + "zii": "~1.1.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + } + } + }, + "ssb-conn-db": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/ssb-conn-db/-/ssb-conn-db-0.3.3.tgz", + "integrity": "sha512-N2C/PweOlzEnrcfhGusnFhrZusfGyEMqip2WpQR/dgLAwHHQK8Wxn9KkC0457AHYlIO3UDH4Q1Mewsz8RLweMw==", + "requires": { + "atomic-file": "^2.1.1", + "debug": "~4.1.1", + "multiserver-address": "~1.0.1", + "pull-notify": "~0.1.1", + "ssb-ref": "~2.13.9" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ssb-ref": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/ssb-ref/-/ssb-ref-2.13.9.tgz", + "integrity": "sha512-TfatNqLvoP+eW/pMIbCmNcaoDq4R2k8jCtWkwDKx4AtluN/LwtyP931d5Mh+2gmzA04W7kxkr6f5ENGgdadMYg==", + "requires": { + "ip": "^1.1.3", + "is-canonical-base64": "^1.1.1", + "is-valid-domain": "~0.0.1", + "multiserver-address": "^1.0.1" + } + } + } + }, + "ssb-conn-hub": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/ssb-conn-hub/-/ssb-conn-hub-0.2.7.tgz", + "integrity": "sha512-fvX7HK44V65C8uMQhTK9B4SHZE7K5P0AU/cHVuDciKPNagEFV0QHKk//JnrrMKHKKvz1msKUxhA0S0iH0S4gqQ==", + "requires": { + "debug": "^4.1.1", + "ip": "^1.1.5", + "multiserver": "^3.4.0", + "multiserver-address": "~1.0.1", + "promisify-tuple": "^1.0.0", + "pull-cat": "~1.1.11", + "pull-notify": "~0.1.1", + "pull-stream": "~3.6.9", + "ssb-ref": "~2.13.9" + }, + "dependencies": { + "ssb-ref": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/ssb-ref/-/ssb-ref-2.13.9.tgz", + "integrity": "sha512-TfatNqLvoP+eW/pMIbCmNcaoDq4R2k8jCtWkwDKx4AtluN/LwtyP931d5Mh+2gmzA04W7kxkr6f5ENGgdadMYg==", + "requires": { + "ip": "^1.1.3", + "is-canonical-base64": "^1.1.1", + "is-valid-domain": "~0.0.1", + "multiserver-address": "^1.0.1" + } + } + } + }, + "ssb-conn-query": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/ssb-conn-query/-/ssb-conn-query-0.4.6.tgz", + "integrity": "sha512-qwa0xYK4r3DwbJQQ/K5umw4VAt3aGTkRbFN0E43Mb33+M94jnXMhDIxZWrPoX1vVQSRZLlCb+laXQiGlxW32AQ==", + "requires": { + "ssb-conn-db": "~0.3.3", + "ssb-conn-hub": "~0.2.7", + "ssb-conn-staging": "~0.1.0" + } + }, + "ssb-conn-staging": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ssb-conn-staging/-/ssb-conn-staging-0.1.0.tgz", + "integrity": "sha512-bFtArSUiF3QrJ9LJ1auonC40pxJd58eBzozBiWsburwbZLUuxqrnW/kCaoyu+PYwTvc0FvMzZR/g4yL2wcE4Yw==", + "requires": { + "debug": "^4.1.1", + "multiserver-address": "~1.0.1", + "pull-cat": "~1.1.11", + "pull-notify": "~0.1.1", + "pull-stream": "^3.6.9" + } + }, + "ssb-db": { + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/ssb-db/-/ssb-db-20.4.0.tgz", + "integrity": "sha512-vsI7w+NZHxBdCVq428U+X0s1+E8Ro8EJ35MwfEpezBLtXlB+X2ej91InukblowiW5rdWD3y4mb01RobhhgTXSw==", + "requires": { + "flumedb": "^2.1.8", + "flumelog-offset": "^3.4.2", + "flumeview-level": "^4.0.4", + "flumeview-reduce": "^1.3.17", + "hashlru": "^2.3.0", + "lodash.clonedeep": "^4.5.0", + "ltgt": "^2.2.0", + "mkdirp": "^1.0.4", + "monotonic-timestamp": "~0.0.8", + "muxrpc-validation": "^3.0.0", + "obv": "0.0.1", + "pull-cat": "^1.1.11", + "pull-cont": "^0.1.1", + "pull-notify": "^0.1.1", + "pull-stream": "^3.4.0", + "rimraf": "^3.0.0", + "ssb-keys": "^7.1.3", + "ssb-msgs": "^5.0.0", + "ssb-ref": "^2.14.0", + "ssb-validate": "^4.0.0", + "zerr": "^1.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ssb-keys": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/ssb-keys/-/ssb-keys-7.2.2.tgz", + "integrity": "sha512-FPeyYU/3LpxcagnbmVWE+Q/qzg6keqeOBPbD7sEH9UKixUASeufPKiORDgh8nVX7J9Z+0vUaHt/WG999kGjvVQ==", + "requires": { + "chloride": "^2.2.8", + "mkdirp": "~0.5.0", + "private-box": "^0.3.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + } + } + }, + "ssb-gossip": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ssb-gossip/-/ssb-gossip-1.1.1.tgz", + "integrity": "sha512-lbizlDBCtOOnbnz7zS81NOtnAyHnXu9E3gxrAJHZe7oyxINRI7IpQ8J79to9aXzkb8+2M32R8K4whmsAHGvJAg==", + "requires": { + "atomic-file": "^1.1.5", + "deep-equal": "^1.0.1", + "has-network": "0.0.1", + "ip": "^1.1.5", + "muxrpc-validation": "^3.0.0", + "on-change-network": "0.0.2", + "on-wakeup": "^1.0.1", + "pull-notify": "^0.1.1", + "pull-ping": "^2.0.2", + "pull-stream": "^3.6.9", + "ssb-ref": "^2.13.9", + "statistics": "^3.3.0" + }, + "dependencies": { + "atomic-file": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/atomic-file/-/atomic-file-1.1.5.tgz", + "integrity": "sha512-TG+5YFiaKQ6CZiSQsosGMJ/IJzwMZ4V/rSdEXlD6+DwKyv8OyeUcprq34kp4yuS6bfQYXhxBC2Vm8PWo+iKBGQ==" + } + } + }, + "ssb-keys": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssb-keys/-/ssb-keys-8.0.1.tgz", + "integrity": "sha512-UrNf65+pCM+XT9kAzKbz438xft3ymRBKFtd0xCCWhyIm5U0CzATw8dhyEBSIbwJ9Ibf4eGZKq1xnEPqlWsf7gA==", + "requires": { + "chloride": "~2.4.0", + "mkdirp": "~0.5.0", + "private-box": "~0.3.0" + } + }, + "ssb-logging": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ssb-logging/-/ssb-logging-1.0.0.tgz", + "integrity": "sha512-6apTG47+VgLAD3MYkpRTbO27DMDh0YLJIvWfcJUEo5FdrjnLsqDl1kkzQ4B5MbH08z5Vklx5907t4by9rhlROQ==", + "requires": { + "bash-color": "0.0.4" + } + }, + "ssb-master": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ssb-master/-/ssb-master-1.0.3.tgz", + "integrity": "sha512-N1Cxm9WscGD9VEZrWbF2amyQai2U2g9gtq57W5zTqbhlQTLUUvl84U9A6fg6GPkECnUXadulnTw+mMYVkLLHjQ==" + }, + "ssb-msgs": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ssb-msgs/-/ssb-msgs-5.2.0.tgz", + "integrity": "sha1-xoHaXNcMV0ySLcpPA8UhU4E1wkM=", + "requires": { + "ssb-ref": "^2.0.0" + } + }, + "ssb-ref": { + "version": "2.14.3", + "resolved": "https://registry.npmjs.org/ssb-ref/-/ssb-ref-2.14.3.tgz", + "integrity": "sha512-XhzVmezsUJLlKxTfWlicxhiPRTEYHfJLskYQNRSnw4USqgo9LVx53+MJAhdZOYpZTW2jINR0TeetWs9M27gcbA==", + "requires": { + "ip": "^1.1.3", + "is-canonical-base64": "^1.1.1", + "is-valid-domain": "~0.0.1", + "multiserver-address": "^1.0.1" + } + }, + "ssb-replicate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/ssb-replicate/-/ssb-replicate-1.3.2.tgz", + "integrity": "sha512-r/34OHn5wDkVUu0UNdKdPjmd3cPDmgknA5nK+gXBZj9ugSDwmdDsfEUavGtA7hlO+He1pC4EXtBa14dqgTqJCg==", + "requires": { + "deep-equal": "^1.0.1", + "observ-debounce": "^1.1.1", + "obv": "0.0.1", + "pull-cat": "^1.1.11", + "pull-next": "^1.0.1", + "pull-notify": "^0.1.1", + "pull-paramap": "^1.2.2", + "pull-pushable": "^2.2.0", + "pull-stream": "^3.6.9", + "ssb-ref": "^2.13.9" + } + }, + "ssb-room": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ssb-room/-/ssb-room-1.3.0.tgz", + "integrity": "sha512-2sYuHgV6fLKWNUjSF0ARcTtFL0C+YpL474M7CGOjC+fJWZ5vg7kcZR/7P3wNyfnqI3Jx2tAGVULF/mjsBmCvaA==", + "requires": { + "body-parser": "^1.16.0", + "debug": "~4.1.1", + "ejs": "^2.5.5", + "express": "^4.16.0", + "pull-cat": "~1.1.11", + "pull-notify": "0.1.1", + "pull-pair": "~1.1.0", + "pull-stream": "~3.6.14", + "qr-image": "^3.2.0", + "secret-stack": "6.3.0", + "ssb-caps": "~1.1.0", + "ssb-client": "~4.7.8", + "ssb-config": "~3.3.2", + "ssb-conn": ">=0.11.0", + "ssb-logging": "~1.0.0", + "ssb-master": "~1.0.3", + "ssb-ref": "~2.13.9" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "secret-stack": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/secret-stack/-/secret-stack-6.3.0.tgz", + "integrity": "sha512-abIfcP1tlJ3gbfgAkp6mgqEc7M70GvXVTyE//iHDXMQz21zQxOD5WcWAiMg1Lgw5FgLfHM1WF+c8PqFNNE3WOg==", + "requires": { + "debug": "^4.1.0", + "hoox": "0.0.1", + "ip": "^1.1.5", + "map-merge": "^1.1.0", + "multiserver": "^3.1.0", + "muxrpc": "^6.4.0", + "non-private-ip": "^1.4.3", + "pull-inactivity": "~2.1.1", + "pull-rate": "^1.0.2", + "pull-stream": "^3.4.5", + "stream-to-pull-stream": "^1.6.1", + "to-camel-case": "^1.0.0" + } + }, + "ssb-config": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ssb-config/-/ssb-config-3.3.3.tgz", + "integrity": "sha512-5dcFuYrUqyELNHzX3fOcgF/SRdjVWjkXxgq5+D+bt7KUy1YCCAEiB1VkQfhwWeDblYQCaXSE6rhuWO9JMTkJSg==", + "requires": { + "deep-extend": "^0.6.0", + "lodash.get": "^4.4.2", + "non-private-ip": "^1.4.4", + "os-homedir": "^1.0.1", + "rc": "^1.1.6", + "ssb-caps": "^1.0.1", + "ssb-keys": "^7.1.4" + } + }, + "ssb-keys": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/ssb-keys/-/ssb-keys-7.2.2.tgz", + "integrity": "sha512-FPeyYU/3LpxcagnbmVWE+Q/qzg6keqeOBPbD7sEH9UKixUASeufPKiORDgh8nVX7J9Z+0vUaHt/WG999kGjvVQ==", + "requires": { + "chloride": "^2.2.8", + "mkdirp": "~0.5.0", + "private-box": "^0.3.0" + } + }, + "ssb-ref": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/ssb-ref/-/ssb-ref-2.13.9.tgz", + "integrity": "sha512-TfatNqLvoP+eW/pMIbCmNcaoDq4R2k8jCtWkwDKx4AtluN/LwtyP931d5Mh+2gmzA04W7kxkr6f5ENGgdadMYg==", + "requires": { + "ip": "^1.1.3", + "is-canonical-base64": "^1.1.1", + "is-valid-domain": "~0.0.1", + "multiserver-address": "^1.0.1" + } + } + } + }, + "ssb-typescript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ssb-typescript/-/ssb-typescript-2.2.0.tgz", + "integrity": "sha512-2xdkB0FyJqjOkmv7r7SOTRP934r2Kz/bHPZlUFk5qStDVekMuTeeOkP+czTXHTgp0GYYKIQmq2ChZroz1C8AJA==" + }, + "ssb-validate": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ssb-validate/-/ssb-validate-4.1.3.tgz", + "integrity": "sha512-g7tOs4nCwHk+G/FZ1N2RmaCkaTNS9hoh/BBP12EH8Jf1PWlkOJtTCak78FHjSTAGFCq/i8Y1ZFQXPNKSK7p3wg==", + "requires": { + "is-canonical-base64": "^1.1.1", + "monotonic-timestamp": "0.0.9", + "ssb-keys": "^7.0.7", + "ssb-ref": "^2.6.2" + }, + "dependencies": { + "ssb-keys": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/ssb-keys/-/ssb-keys-7.2.2.tgz", + "integrity": "sha512-FPeyYU/3LpxcagnbmVWE+Q/qzg6keqeOBPbD7sEH9UKixUASeufPKiORDgh8nVX7J9Z+0vUaHt/WG999kGjvVQ==", + "requires": { + "chloride": "^2.2.8", + "mkdirp": "~0.5.0", + "private-box": "^0.3.0" + } + } + } + }, + "statistics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/statistics/-/statistics-3.3.0.tgz", + "integrity": "sha1-7HtHUP8DqySmTdmzV6eDFr6teKo=" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-to-pull-stream": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz", + "integrity": "sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg==", + "requires": { + "looper": "^3.0.0", + "pull-stream": "^3.2.3" + } + }, + "string.prototype.trim": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.3.tgz", + "integrity": "sha512-16IL9pIBA5asNOSukPfxX2W68BaBvxyiRK16H3RA/lWW9BDosh+w7f+LhomPHpXJ82QEe7w7/rY/S1CV97raLg==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tape": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.1.1.tgz", + "integrity": "sha512-ujhT+ZJPqSGY9Le02mIGBnyWo7Ks05FEGS9PnlqECr3sM3KyV4CSCXAvSBJKMN+t+aZYLKEFUEo0l4wFJMhppQ==", + "requires": { + "call-bind": "^1.0.0", + "deep-equal": "^2.0.5", + "defined": "^1.0.0", + "dotignore": "^0.1.2", + "for-each": "^0.3.3", + "glob": "^7.1.6", + "has": "^1.0.3", + "inherits": "^2.0.4", + "is-regex": "^1.1.1", + "minimist": "^1.2.5", + "object-inspect": "^1.9.0", + "object-is": "^1.1.4", + "object.assign": "^4.1.2", + "resolve": "^1.19.0", + "resumer": "^0.0.0", + "string.prototype.trim": "^1.2.3", + "through": "^2.3.8" + }, + "dependencies": { + "deep-equal": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", + "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", + "requires": { + "call-bind": "^1.0.0", + "es-get-iterator": "^1.1.1", + "get-intrinsic": "^1.0.1", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.1.1", + "isarray": "^2.0.5", + "object-is": "^1.1.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "to-camel-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", + "integrity": "sha1-GlYFSy+daWKYzmamCJcyK29CPkY=", + "requires": { + "to-space-case": "^1.0.0" + } + }, + "to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + }, + "to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "requires": { + "to-no-case": "^1.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "tweetnacl-auth": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tweetnacl-auth/-/tweetnacl-auth-0.3.1.tgz", + "integrity": "sha1-t1vC3xVkm7hOi5qjwGacbEvODSU=", + "requires": { + "tweetnacl": "0.x.x" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uint48be": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uint48be/-/uint48be-2.0.1.tgz", + "integrity": "sha512-LQvWofTo3RCz+XaQR3VNch+dDFwpIvWr/98imhQne++vFhpQP16YAC/a8w9N00Heqqra00ACjHT18cgvn5H+bg==" + }, + "ultron": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", + "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", + "requires": { + "options": ">=0.0.5", + "ultron": "1.0.x" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "zerr": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/zerr/-/zerr-1.0.4.tgz", + "integrity": "sha1-YoFN15nv+DYfKiKPQfcFxeGd5Mk=" + }, + "zii": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/zii/-/zii-1.1.0.tgz", + "integrity": "sha512-l4EKO8dgLsEWxdb/koCjv92FplAIAQaV4Riq5n/38dTC7Z3NwaFSKQ5Bda+CUTu11VtFE1D+4+HPfPt7/86CRw==" + } + } +} diff --git a/test/nodejs/package.json b/test/nodejs/package.json new file mode 100644 index 0000000..3ae7664 --- /dev/null +++ b/test/nodejs/package.json @@ -0,0 +1,24 @@ +{ + "name": "go-ssb-rooms-tests", + "version": "1.0.1", + "description": "tests between go and ssb-js", + "main": "sbot_client.js", + "scripts": { + "test": "go test" + }, + "author": "cryptix", + "dependencies": { + "run-parallel": "^1.1.9", + "run-series": "^1.1.9", + "secret-stack": "^6.3.1", + "sodium-native": "^3.2.0", + "ssb-config": "^3.4.5", + "ssb-conn": "^0.19.1", + "ssb-db": "^20.3.0", + "ssb-gossip": "^1.1.1", + "ssb-keys": "^8.0.0", + "ssb-replicate": "^1.3.2", + "ssb-room": "^1.3.0", + "tape": "^5.0.1" + } +} diff --git a/test/nodejs/sbot_client.js b/test/nodejs/sbot_client.js new file mode 100644 index 0000000..7e0fff9 --- /dev/null +++ b/test/nodejs/sbot_client.js @@ -0,0 +1,86 @@ +const Path = require('path') +const { readFileSync } = require('fs') +const { loadOrCreateSync } = require('ssb-keys') +const pull = require('pull-stream') // used in eval scripts +const tape = require('tape') +const parallel = require('run-parallel') // used in eval scripts +const theStack = require('secret-stack') +const ssbCaps = require('ssb-caps') + +const testSHSappKey = bufFromEnv('TEST_APPKEY') + +let testAppkey = Buffer.from(ssbCaps.shs, 'base64') +if (testSHSappKey !== false) { + testAppkey = testSHSappKey +} + + +const createSbot = theStack({caps: {shs: testAppkey } }) + .use(require('ssb-db')) + .use(require('ssb-gossip')) + .use(require('ssb-replicate')) + .use(require('ssb-conn')) + .use(require('ssb-room/tunnel/client')) + +const testName = process.env.TEST_NAME +const testBob = process.env.TEST_BOB +const testAddr = process.env.TEST_GOADDR + +const scriptBefore = readFileSync(process.env.TEST_BEFORE).toString() +const scriptAfter = readFileSync(process.env.TEST_AFTER).toString() + +function bufFromEnv(evname) { + const has = process.env[evname] + if (has) { + return Buffer.from(has, 'base64') + } + return false +} + +tape.createStream().pipe(process.stderr) +tape(testName, function (t) { + + let timeoutLength = 15000 + var tapeTimeout = null + function run() { // needs to be called by the before block when it's done + t.timeoutAfter(timeoutLength) // doesn't exit the process + tapeTimeout = setTimeout(() => { + t.comment('test timeout') + process.exit(1) + }, timeoutLength*1.25) + const to = `net:${testAddr}~shs:${testBob.substr(1).replace('.ed25519', '')}` + t.comment('dialing:' + to) + sbot.connect(to, (err) => { + t.error(err, 'connected') + eval(scriptAfter) + }) + } + + function exit() { // call this when you're done + sbot.close() + t.comment('closed sbot') + clearTimeout(tapeTimeout) + t.end() + process.exit(0) + } + + const tempRepo = Path.join('testrun', testName) + const keys = loadOrCreateSync(Path.join(tempRepo, 'secret')) + const opts = { + allowPrivate: true, + path: tempRepo, + keys: keys + } + + if (testSHSappKey !== false) { + opts.caps = opts.caps ? opts.caps : {} + opts.caps.shs = testSHSappKey + } + + const sbot = createSbot(opts) + const alice = sbot.whoami() + + t.comment('sbot spawned, running before') + console.log(alice.id) // tell go process who's incoming + eval(scriptBefore) +}) diff --git a/test/nodejs/sbot_serv.js b/test/nodejs/sbot_serv.js new file mode 100644 index 0000000..6b8d218 --- /dev/null +++ b/test/nodejs/sbot_serv.js @@ -0,0 +1,81 @@ +const Path = require('path') +const tape = require('tape') +const { readFileSync } = require('fs') +const { loadOrCreateSync } = require('ssb-keys') +const theStack = require('secret-stack') +const ssbCaps = require('ssb-caps') + +const testSHSappKey = bufFromEnv('TEST_APPKEY') + +let testAppkey = Buffer.from(ssbCaps.shs, 'base64') +if (testSHSappKey !== false) { + testAppkey = testSHSappKey +} + +stackOpts = {appKey: require('ssb-caps').shs} +// stackOpts = {caps: {shs: testAppkey } } +const createSbot = theStack(stackOpts) + .use(require('ssb-db')) + .use(require('ssb-master')) + .use(require('ssb-logging')) + .use(require('ssb-conn')) + .use(require('ssb-room/tunnel/server')) + +const testName = process.env['TEST_NAME'] +const testPort = process.env['TEST_PORT'] + +const testSession = require(process.env['TEST_BEFORE']) +// const scriptBefore = readFileSync( +// const scriptAfter = readFileSync(process.env['TEST_AFTER']).toString() + +tape.createStream().pipe(process.stderr); +tape(testName, function (t) { + // t.timeoutAfter(30000) // doesn't exit the process +// const tapeTimeout = setTimeout(() => { +// t.comment("test timeout") +// process.exit(1) +// }, 50000) + + + + function exit() { // call this when you're done + sbot.close() + t.comment('closed jsbot') + // clearTimeout(tapeTimeout) + t.end() + } + + const tempRepo = Path.join('testrun', testName) + const keys = loadOrCreateSync(Path.join(tempRepo, 'secret')) + const sbot = createSbot({ + port: testPort, + path: tempRepo, + keys: keys, + }) + const alice = sbot.whoami() + +// const replicate_changes = sbot.replicate.changes() + + t.comment("sbot spawned, running before") + + function ready() { + console.warn('ready!', alice.id) + console.log(alice.id) // tell go process who our pubkey + } + testSession.before(sbot, ready) + + + sbot.on("rpc:connect", (remote, isClient) => { + t.comment("new connection: "+ remote.id) + testSession.after(sbot, remote) + }) +}) + +// util +function bufFromEnv(evname) { + const has = process.env[evname] + if (has) { + return Buffer.from(has, 'base64') + } + return false +} \ No newline at end of file diff --git a/test/nodejs/setup_test.go b/test/nodejs/setup_test.go index c96d3f1..cb51c4d 100644 --- a/test/nodejs/setup_test.go +++ b/test/nodejs/setup_test.go @@ -1 +1,261 @@ +// SPDX-License-Identifier: MIT + +// Package nodejs_test contains test scenarios and helpers to run interoparability tests against the javascript implementation. package nodejs_test + +import ( + "bufio" + "context" + "crypto/rand" + "encoding/base64" + "fmt" + "io/ioutil" + mrand "math/rand" + "net" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/go-kit/kit/log" + "github.com/stretchr/testify/require" + "go.cryptoscope.co/muxrpc/v2/debug" + "go.cryptoscope.co/netwrap" + refs "go.mindeco.de/ssb-refs" + + "go.mindeco.de/ssb-rooms/internal/maybemod/testutils" + "go.mindeco.de/ssb-rooms/roomsrv" +) + +func init() { + err := os.RemoveAll("testrun") + if err != nil { + fmt.Println("failed to clean testrun dir") + panic(err) + } +} + +func writeFile(t *testing.T, data string) string { + r := require.New(t) + f, err := ioutil.TempFile("testrun/"+t.Name(), "*.js") + r.NoError(err) + _, err = fmt.Fprintf(f, "%s", data) + r.NoError(err) + err = f.Close() + r.NoError(err) + return f.Name() +} + +type testSession struct { + t *testing.T + + info log.Logger + + repo string + + keySHS, keyHMAC []byte + + // since we can't pass *testing.T to other goroutines, we use this to collect errors from background taskts + backgroundErrs []<-chan error + + gobot *roomsrv.Server + + done errgroup.Group + // doneJS, doneGo <-chan struct{} + + ctx context.Context + cancel context.CancelFunc +} + +// TODO: restrucuture so that we can test both (default and random net keys) with the same Code + +// rolls random values for secret-handshake app-key and HMAC +func newRandomSession(t *testing.T) *testSession { + appKey := make([]byte, 32) + rand.Read(appKey) + hmacKey := make([]byte, 32) + rand.Read(hmacKey) + return newSession(t, appKey, hmacKey) +} + +// if appKey is nil, the default value is used +// if hmac is nil, the object string is signed instead +func newSession(t *testing.T, appKey, hmacKey []byte) *testSession { + repo := filepath.Join("testrun", t.Name()) + err := os.RemoveAll(repo) + if err != nil { + t.Errorf("remove testrun folder (%s) for this test: %s", repo, err) + } + + ts := &testSession{ + info: testutils.NewRelativeTimeLogger(nil), + repo: repo, + t: t, + keySHS: appKey, + keyHMAC: hmacKey, + } + + // todo: hook into deadline + ts.ctx, ts.cancel = context.WithCancel(context.Background()) + + return ts +} + +func (ts *testSession) startGoServer(opts ...roomsrv.Option) { + r := require.New(ts.t) + + // prepend defaults + opts = append([]roomsrv.Option{ + roomsrv.WithLogger(ts.info), + roomsrv.WithListenAddr("localhost:0"), + roomsrv.WithRepoPath(ts.repo), + roomsrv.WithContext(ts.ctx), + }, opts...) + + if ts.keySHS != nil { + opts = append(opts, roomsrv.WithAppKey(ts.keySHS)) + } + + opts = append(opts, + roomsrv.WithPostSecureConnWrapper(func(conn net.Conn) (net.Conn, error) { + return debug.WrapDump(filepath.Join("testrun", ts.t.Name(), "muxdump"), conn) + }), + ) + + srv, err := roomsrv.New(opts...) + r.NoError(err, "failed to init tees a server") + ts.t.Logf("go server: %s", srv.Whoami()) + ts.t.Cleanup(func() { + srv.Close() + }) + + ts.done.Go(func() error { + err := srv.Network.Serve(ts.ctx) + if err != nil { + err = fmt.Errorf("node serve exited: %w", err) + ts.t.Log(err) + return err + } + return nil + }) + + ts.gobot = srv + + // TODO: make muxrpc client and connect to whoami for _ready_ ? + return +} + +var jsBotCnt = 0 + +func (ts *testSession) startJSBot(jsbefore, jsafter string) refs.FeedRef { + return ts.startJSBotWithName("", jsbefore, jsafter) +} + +// returns the jsbots pubkey +func (ts *testSession) startJSBotWithName(name, jsbefore, jsafter string) refs.FeedRef { + ts.t.Log("starting client", name) + r := require.New(ts.t) + cmd := exec.CommandContext(ts.ctx, "node", "./sbot_client.js") + cmd.Stderr = os.Stderr + + outrc, err := cmd.StdoutPipe() + r.NoError(err) + + if name == "" { + name = fmt.Sprint(ts.t.Name(), jsBotCnt) + } + jsBotCnt++ + env := []string{ + "TEST_NAME=" + name, + "TEST_BOB=" + ts.gobot.Whoami().Ref(), + "TEST_GOADDR=" + netwrap.GetAddr(ts.gobot.Network.GetListenAddr(), "tcp").String(), + "TEST_BEFORE=" + writeFile(ts.t, jsbefore), + "TEST_AFTER=" + writeFile(ts.t, jsafter), + } + + if ts.keySHS != nil { + env = append(env, "TEST_APPKEY="+base64.StdEncoding.EncodeToString(ts.keySHS)) + } + if ts.keyHMAC != nil { + env = append(env, "TEST_HMACKEY="+base64.StdEncoding.EncodeToString(ts.keyHMAC)) + } + cmd.Env = env + r.NoError(cmd.Start(), "failed to init test js-sbot") + + ts.done.Go(cmd.Wait) + ts.t.Cleanup(func() { + cmd.Process.Kill() + }) + + pubScanner := bufio.NewScanner(outrc) // TODO muxrpc comms? + r.True(pubScanner.Scan(), "multiple lines of output from js - expected #1 to be %s pubkey/id", name) + + jsBotRef, err := refs.ParseFeedRef(pubScanner.Text()) + r.NoError(err, "failed to get %s key from JS process") + ts.t.Logf("JS %s:%d %s", name, jsBotCnt, jsBotRef.Ref()) + return *jsBotRef +} + +func (ts *testSession) startJSBotAsServer(name, testScriptFileName string) (*refs.FeedRef, int) { + ts.t.Log("starting srv", name) + r := require.New(ts.t) + cmd := exec.CommandContext(ts.ctx, "node", "./sbot_serv.js") + cmd.Stderr = os.Stderr + + outrc, err := cmd.StdoutPipe() + r.NoError(err) + + if name == "" { + name = fmt.Sprintf("jsbot-%d", jsBotCnt) + } + jsBotCnt++ + + var port = 1024 + mrand.Intn(23000) + + env := []string{ + "TEST_NAME=" + filepath.Join(ts.t.Name(), "jsbot-"+name), + "TEST_BOB=" + ts.gobot.Whoami().Ref(), + fmt.Sprintf("TEST_PORT=%d", port), + "TEST_BEFORE=" + testScriptFileName, + } + // if ts.keySHS != nil { + // env = append(env, "TEST_APPKEY="+base64.StdEncoding.EncodeToString(ts.keySHS)) + // } + cmd.Env = env + + r.NoError(cmd.Start(), "failed to init test js-sbot") + + ts.done.Go(cmd.Wait) + ts.t.Cleanup(func() { + cmd.Process.Kill() + }) + + pubScanner := bufio.NewScanner(outrc) // TODO muxrpc comms? + r.True(pubScanner.Scan(), "multiple lines of output from js - expected #1 to be %s pubkey/id", name) + + srvRef, err := refs.ParseFeedRef(pubScanner.Text()) + r.NoError(err, "failed to get srvRef key from JS process") + ts.t.Logf("JS %s: %s port: %d", name, srvRef.Ref(), port) + return srvRef, port +} + +func (ts *testSession) wait() { + closeErrc := make(chan error) + + go func() { + time.Sleep(15 * time.Second) // would be nice to get -test.timeout for this + + ts.gobot.Shutdown() + closeErrc <- ts.gobot.Close() + close(closeErrc) + }() + + for err := range testutils.MergeErrorChans(append(ts.backgroundErrs, closeErrc)...) { + require.NoError(ts.t, err) + } + + require.NoError(ts.t, ts.done.Wait()) +} diff --git a/test/nodejs/testscripts/server.js b/test/nodejs/testscripts/server.js new file mode 100644 index 0000000..4271ec0 --- /dev/null +++ b/test/nodejs/testscripts/server.js @@ -0,0 +1,11 @@ +module.exports = { + before: (sbot, ready) => { + console.warn('before:', sbot.id) + setTimeout(ready, 1000) + // ready() + }, + + after: (sbot, client) => { + console.warn('after:', sbot.id, client.id) + } +} \ No newline at end of file