diff --git a/.gitignore b/.gitignore index 0fc9f902957a..ad7d438fa7b5 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,4 @@ debug_container.log *.synctex.gz /x/genutil/config/priv_validator_key.json /x/genutil/data/priv_validator_state.json +.env.local diff --git a/baseapp/streaming.go b/baseapp/streaming.go index c978d959aa7c..da49575c4034 100644 --- a/baseapp/streaming.go +++ b/baseapp/streaming.go @@ -1,17 +1,24 @@ package baseapp import ( + "context" "fmt" "sort" "strings" "github.com/spf13/cast" + "cosmossdk.io/schema" + "cosmossdk.io/schema/appdata" + "cosmossdk.io/schema/decoding" + "cosmossdk.io/schema/indexing" "cosmossdk.io/store/streaming" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/client/flags" servertypes "github.com/cosmos/cosmos-sdk/server/types" + + abci "github.com/cometbft/cometbft/api/cometbft/abci/v1" ) const ( @@ -22,6 +29,33 @@ const ( StreamingABCIStopNodeOnErrTomlKey = "stop-node-on-err" ) +func (app *BaseApp) EnableIndexer(indexerOpts interface{}, keys map[string]*storetypes.KVStoreKey, appModules map[string]any) error { + optsMap, ok := indexerOpts.(map[string]interface{}) + if !ok { + return fmt.Errorf("invalid indexer options type %T, expected a map", indexerOpts) + } + + listener, err := indexing.Start(indexing.Options{ + Options: optsMap, + Resolver: decoding.ModuleSetDecoderResolver(appModules), + SyncSource: nil, + Logger: app.logger.With("module", "indexer"), + }) + if err != nil { + return err + } + + exposedKeys := exposeStoreKeysSorted([]string{"*"}, keys) + app.cms.AddListeners(exposedKeys) + + app.streamingManager = storetypes.StreamingManager{ + ABCIListeners: []storetypes.ABCIListener{listenerWrapper{listener}}, + StopNodeOnErr: true, + } + + return nil +} + // RegisterStreamingServices registers streaming services with the BaseApp. func (app *BaseApp) RegisterStreamingServices(appOpts servertypes.AppOptions, keys map[string]*storetypes.KVStoreKey) error { // register streaming services @@ -110,3 +144,51 @@ func exposeStoreKeysSorted(keysStr []string, keys map[string]*storetypes.KVStore return exposeStoreKeys } + +type listenerWrapper struct { + listener appdata.Listener +} + +func (p listenerWrapper) ListenFinalizeBlock(_ context.Context, req abci.FinalizeBlockRequest, res abci.FinalizeBlockResponse) error { + if p.listener.StartBlock != nil { + err := p.listener.StartBlock(appdata.StartBlockData{ + Height: uint64(req.Height), + }) + if err != nil { + return err + } + } + + //// TODO txs, events + + return nil +} + +func (p listenerWrapper) ListenCommit(ctx context.Context, res abci.CommitResponse, changeSet []*storetypes.StoreKVPair) error { + if cb := p.listener.OnKVPair; cb != nil { + updates := make([]appdata.ModuleKVPairUpdate, len(changeSet)) + for i, pair := range changeSet { + updates[i] = appdata.ModuleKVPairUpdate{ + ModuleName: pair.StoreKey, + Update: schema.KVPairUpdate{ + Key: pair.Key, + Value: pair.Value, + Delete: pair.Delete, + }, + } + } + err := cb(appdata.KVPairData{Updates: updates}) + if err != nil { + return err + } + } + + if p.listener.Commit != nil { + err := p.listener.Commit(appdata.CommitData{}) + if err != nil { + return err + } + } + + return nil +} diff --git a/codec/collections.go b/codec/collections.go index 0137a989e790..cc05dd759c9f 100644 --- a/codec/collections.go +++ b/codec/collections.go @@ -8,6 +8,9 @@ import ( gogotypes "github.com/cosmos/gogoproto/types" "google.golang.org/protobuf/encoding/protojson" protov2 "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + "cosmossdk.io/schema" "cosmossdk.io/collections" collcodec "cosmossdk.io/collections/codec" @@ -91,6 +94,16 @@ func (c collValue[T, PT]) ValueType() string { return "github.com/cosmos/gogoproto/" + c.messageName } +func (c collValue[T, PT]) SchemaColumns() []schema.Field { + var pt PT + msgName := proto.MessageName(pt) + desc, err := proto.HybridResolver.FindDescriptorByName(protoreflect.FullName(msgName)) + if err != nil { + panic(fmt.Errorf("could not find descriptor for %s: %w", msgName, err)) + } + return protoCols(desc.(protoreflect.MessageDescriptor)) +} + type protoMessageV2[T any] interface { *T protov2.Message @@ -136,6 +149,11 @@ func (c collValue2[T, PT]) ValueType() string { return "google.golang.org/protobuf/" + c.messageName } +func (c collValue2[T, PT]) SchemaColumns() []schema.Field { + var pt PT + return protoCols(pt.ProtoReflect().Descriptor()) +} + // CollInterfaceValue instantiates a new collections.ValueCodec for a generic // interface value. The codec must be able to marshal and unmarshal the // interface. @@ -179,3 +197,70 @@ func (c collInterfaceValue[T]) ValueType() string { var t T return fmt.Sprintf("%T", t) } + +func protoCols(desc protoreflect.MessageDescriptor) []schema.Field { + nFields := desc.Fields() + cols := make([]schema.Field, 0, nFields.Len()) + for i := 0; i < nFields.Len(); i++ { + f := nFields.Get(i) + cols = append(cols, protoCol(f)) + } + return cols +} + +func protoCol(f protoreflect.FieldDescriptor) schema.Field { + col := schema.Field{Name: string(f.Name())} + + if f.IsMap() || f.IsList() { + col.Kind = schema.JSONKind + col.Nullable = true + } else { + switch f.Kind() { + case protoreflect.BoolKind: + col.Kind = schema.BoolKind + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + col.Kind = schema.Int32Kind + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + col.Kind = schema.Int64Kind + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + col.Kind = schema.Int64Kind + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + col.Kind = schema.IntegerStringKind + case protoreflect.FloatKind: + col.Kind = schema.Float32Kind + case protoreflect.DoubleKind: + col.Kind = schema.Float64Kind + case protoreflect.StringKind: + col.Kind = schema.StringKind + case protoreflect.BytesKind: + col.Kind = schema.BytesKind + case protoreflect.EnumKind: + col.Kind = schema.EnumKind + enumDesc := f.Enum() + var vals []string + n := enumDesc.Values().Len() + for i := 0; i < n; i++ { + vals = append(vals, string(enumDesc.Values().Get(i).Name())) + } + col.EnumDefinition = schema.EnumDefinition{ + Name: string(enumDesc.Name()), + Values: vals, + } + case protoreflect.MessageKind: + col.Nullable = true + fullName := f.Message().FullName() + if fullName == "google.protobuf.Timestamp" { + col.Kind = schema.TimeKind + } else if fullName == "google.protobuf.Duration" { + col.Kind = schema.DurationKind + } else { + col.Kind = schema.JSONKind + } + } + if f.HasPresence() { + col.Nullable = true + } + } + + return col +} diff --git a/collections/codec/alternative_value_test.go b/collections/codec/alternative_value_test.go index 358395427b90..871d95d653ce 100644 --- a/collections/codec/alternative_value_test.go +++ b/collections/codec/alternative_value_test.go @@ -17,7 +17,7 @@ type altValue struct { func TestAltValueCodec(t *testing.T) { // we assume we want to migrate the value from json(altValue) to just be // the raw value uint64. - canonical := codec.KeyToValueCodec(codec.NewUint64Key[uint64]()) + canonical := codec.KeyToValueCodec(codec.KeyCodec[uint64](codec.NewUint64Key[uint64]())) alternative := func(v []byte) (uint64, error) { var alt altValue err := json.Unmarshal(v, &alt) diff --git a/collections/codec/bool.go b/collections/codec/bool.go index 827af36c0715..f5a4616ca00b 100644 --- a/collections/codec/bool.go +++ b/collections/codec/bool.go @@ -6,9 +6,11 @@ import ( "strconv" ) -func NewBoolKey[T ~bool]() KeyCodec[T] { return boolKey[T]{} } +func NewBoolKey[T ~bool]() NameableKeyCodec[T] { return boolKey[T]{} } -type boolKey[T ~bool] struct{} +type boolKey[T ~bool] struct { + name string +} func (b boolKey[T]) Encode(buffer []byte, key T) (int, error) { if key { @@ -64,3 +66,12 @@ func (b boolKey[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { func (b boolKey[T]) SizeNonTerminal(key T) int { return b.Size(key) } + +func (b boolKey[T]) WithName(name string) NamedKeyCodec[T] { + b.name = name + return b +} + +func (b boolKey[T]) Name() string { + return b.name +} diff --git a/collections/codec/bytes.go b/collections/codec/bytes.go index 28334795e365..87b42046bcb0 100644 --- a/collections/codec/bytes.go +++ b/collections/codec/bytes.go @@ -10,9 +10,11 @@ import ( // using the BytesKey KeyCodec. const MaxBytesKeyNonTerminalSize = math.MaxUint8 -func NewBytesKey[T ~[]byte]() KeyCodec[T] { return bytesKey[T]{} } +func NewBytesKey[T ~[]byte]() NameableKeyCodec[T] { return bytesKey[T]{} } -type bytesKey[T ~[]byte] struct{} +type bytesKey[T ~[]byte] struct { + name string +} func (b bytesKey[T]) Encode(buffer []byte, key T) (int, error) { return copy(buffer, key), nil @@ -77,3 +79,12 @@ func (bytesKey[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { func (bytesKey[T]) SizeNonTerminal(key T) int { return len(key) + 1 } + +func (b bytesKey[T]) WithName(name string) NamedKeyCodec[T] { + b.name = name + return b +} + +func (b bytesKey[T]) Name() string { + return b.name +} diff --git a/collections/codec/codec.go b/collections/codec/codec.go index 2988c9f52425..83fe732bd55c 100644 --- a/collections/codec/codec.go +++ b/collections/codec/codec.go @@ -3,6 +3,8 @@ package codec import ( "errors" "fmt" + + "cosmossdk.io/schema" ) var ErrEncoding = errors.New("collections: encoding error") @@ -74,6 +76,22 @@ type ValueCodec[T any] interface { ValueType() string } +// IndexableCodec is an interface that all codec's should implement in order to properly support indexing. +// It is not required by KeyCodec or ValueCodec in order to preserve backwards compatibility, but +// a future version of collections may make it required and all codec's should aim to implement it. +// If it is not implemented, fallback defaults will be used for indexing that may be sub-optimal. +// +// Implementations of IndexableCodec should test that they are conformant using the schema.ValidateWithKeyFields +// and schema.ValidateWithValueFields depending on whether the codec is a KeyCodec or ValueCodec respectively. +type IndexableCodec[T any] interface { + LogicalDecoder() (LogicalDecoder[T], error) +} + +type LogicalDecoder[T any] struct { + Fields []schema.Field + ToSchemaType func(T) (any, error) +} + // NewUntypedValueCodec returns an UntypedValueCodec for the provided ValueCodec. func NewUntypedValueCodec[V any](v ValueCodec[V]) UntypedValueCodec { typeName := fmt.Sprintf("%T", *new(V)) diff --git a/collections/codec/int.go b/collections/codec/int.go index 1300efde4df0..5ee3e6fd6d6e 100644 --- a/collections/codec/int.go +++ b/collections/codec/int.go @@ -7,9 +7,11 @@ import ( "strconv" ) -func NewInt64Key[T ~int64]() KeyCodec[T] { return int64Key[T]{} } +func NewInt64Key[T ~int64]() NameableKeyCodec[T] { return int64Key[T]{} } -type int64Key[T ~int64] struct{} +type int64Key[T ~int64] struct { + name string +} func (i int64Key[T]) Encode(buffer []byte, key T) (int, error) { binary.BigEndian.PutUint64(buffer, (uint64)(key)) @@ -64,11 +66,22 @@ func (i int64Key[T]) SizeNonTerminal(_ T) int { return 8 } -func NewInt32Key[T ~int32]() KeyCodec[T] { +func (i int64Key[T]) WithName(name string) NamedKeyCodec[T] { + i.name = name + return i +} + +func (i int64Key[T]) Name() string { + return i.name +} + +func NewInt32Key[T ~int32]() NameableKeyCodec[T] { return int32Key[T]{} } -type int32Key[T ~int32] struct{} +type int32Key[T ~int32] struct { + name string +} func (i int32Key[T]) Encode(buffer []byte, key T) (int, error) { binary.BigEndian.PutUint32(buffer, (uint32)(key)) @@ -121,3 +134,12 @@ func (i int32Key[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { func (i int32Key[T]) SizeNonTerminal(_ T) int { return 4 } + +func (i int32Key[T]) WithName(name string) NamedKeyCodec[T] { + i.name = name + return i +} + +func (i int32Key[T]) Name() string { + return i.name +} diff --git a/collections/codec/naming.go b/collections/codec/naming.go new file mode 100644 index 000000000000..fe60ee0e8cae --- /dev/null +++ b/collections/codec/naming.go @@ -0,0 +1,35 @@ +package codec + +type HasName interface { + // Name returns the name of key in the schema if one is defined or the empty string. + // Multipart keys should separate names with commas, i.e. "name1,name2". + Name() string +} + +// NameableKeyCodec is a KeyCodec that can be named. +type NameableKeyCodec[T any] interface { + KeyCodec[T] + + // WithName returns the KeyCodec with the provided name. + WithName(name string) NamedKeyCodec[T] +} + +// NamedKeyCodec is a KeyCodec that has a name. +type NamedKeyCodec[T any] interface { + KeyCodec[T] + HasName +} + +// NameableValueCodec is a ValueCodec that can be named. +type NameableValueCodec[T any] interface { + ValueCodec[T] + + // WithName returns the ValueCodec with the provided name. + WithName(name string) NamedValueCodec[T] +} + +// NamedValueCodec is a ValueCodec that has a name. +type NamedValueCodec[T any] interface { + ValueCodec[T] + HasName +} diff --git a/collections/codec/string.go b/collections/codec/string.go index 3189b8bc9cbf..3c9d53b1c42b 100644 --- a/collections/codec/string.go +++ b/collections/codec/string.go @@ -6,14 +6,16 @@ import ( "fmt" ) -func NewStringKeyCodec[T ~string]() KeyCodec[T] { return stringKey[T]{} } +func NewStringKeyCodec[T ~string]() NameableKeyCodec[T] { return stringKey[T]{} } const ( // StringDelimiter defines the delimiter of a string key when used in non-terminal encodings. StringDelimiter uint8 = 0x0 ) -type stringKey[T ~string] struct{} +type stringKey[T ~string] struct { + name string +} func (stringKey[T]) Encode(buffer []byte, key T) (int, error) { return copy(buffer, key), nil @@ -66,3 +68,12 @@ func (stringKey[T]) Stringify(key T) string { func (stringKey[T]) KeyType() string { return "string" } + +func (s stringKey[T]) WithName(name string) NamedKeyCodec[T] { + s.name = name + return s +} + +func (s stringKey[T]) Name() string { + return s.name +} diff --git a/collections/codec/uint.go b/collections/codec/uint.go index 658235d385ad..8586aeb1b6ff 100644 --- a/collections/codec/uint.go +++ b/collections/codec/uint.go @@ -7,9 +7,11 @@ import ( "strconv" ) -func NewUint64Key[T ~uint64]() KeyCodec[T] { return uint64Key[T]{} } +func NewUint64Key[T ~uint64]() NameableKeyCodec[T] { return uint64Key[T]{} } -type uint64Key[T ~uint64] struct{} +type uint64Key[T ~uint64] struct { + name string +} func (uint64Key[T]) Encode(buffer []byte, key T) (int, error) { binary.BigEndian.PutUint64(buffer, (uint64)(key)) @@ -55,9 +57,20 @@ func (uint64Key[T]) KeyType() string { return "uint64" } -func NewUint32Key[T ~uint32]() KeyCodec[T] { return uint32Key[T]{} } +func (u uint64Key[T]) WithName(name string) NamedKeyCodec[T] { + u.name = name + return u +} + +func (u uint64Key[T]) Name() string { + return u.name +} + +func NewUint32Key[T ~uint32]() NameableKeyCodec[T] { return uint32Key[T]{} } -type uint32Key[T ~uint32] struct{} +type uint32Key[T ~uint32] struct { + name string +} func (uint32Key[T]) Encode(buffer []byte, key T) (int, error) { binary.BigEndian.PutUint32(buffer, (uint32)(key)) @@ -95,9 +108,20 @@ func (u uint32Key[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { return func (uint32Key[T]) SizeNonTerminal(_ T) int { return 4 } -func NewUint16Key[T ~uint16]() KeyCodec[T] { return uint16Key[T]{} } +func (u uint32Key[T]) WithName(name string) NamedKeyCodec[T] { + u.name = name + return u +} -type uint16Key[T ~uint16] struct{} +func (u uint32Key[T]) Name() string { + return u.name +} + +func NewUint16Key[T ~uint16]() NameableKeyCodec[T] { return uint16Key[T]{} } + +type uint16Key[T ~uint16] struct { + name string +} func (uint16Key[T]) Encode(buffer []byte, key T) (int, error) { binary.BigEndian.PutUint16(buffer, (uint16)(key)) @@ -135,6 +159,15 @@ func (u uint16Key[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { return func (u uint16Key[T]) SizeNonTerminal(key T) int { return u.Size(key) } +func (u uint16Key[T]) WithName(name string) NamedKeyCodec[T] { + u.name = name + return u +} + +func (u uint16Key[T]) Name() string { + return u.name +} + func uintEncodeJSON(value uint64) ([]byte, error) { str := `"` + strconv.FormatUint(value, 10) + `"` return []byte(str), nil diff --git a/collections/collections.go b/collections/collections.go index 9de3bbc38226..05bfcca2a2be 100644 --- a/collections/collections.go +++ b/collections/collections.go @@ -6,6 +6,8 @@ import ( "io" "math" + "cosmossdk.io/schema" + "cosmossdk.io/collections/codec" ) @@ -90,6 +92,16 @@ type Collection interface { ValueCodec() codec.UntypedValueCodec genesisHandler + + logicalDecoder() (logicalDecoder, error) + + isSecondaryIndex() bool +} + +type logicalDecoder struct { + objectType schema.ObjectType + keyDecoder func([]byte) (any, error) + valueDecoder func([]byte) (any, error) } // Prefix defines a segregation bytes namespace for specific collections objects. @@ -157,3 +169,5 @@ func (c collectionImpl[K, V]) exportGenesis(ctx context.Context, w io.Writer) er } func (c collectionImpl[K, V]) defaultGenesis(w io.Writer) error { return c.m.defaultGenesis(w) } + +func (c collectionImpl[K, V]) isSecondaryIndex() bool { return c.m.isSecondaryIndex } diff --git a/collections/go.mod b/collections/go.mod index ea72a4d5ec89..1cbf81c30423 100644 --- a/collections/go.mod +++ b/collections/go.mod @@ -5,7 +5,9 @@ go 1.21 require ( cosmossdk.io/core v0.12.0 cosmossdk.io/core/testing v0.0.0-00010101000000-000000000000 + cosmossdk.io/schema v0.0.0 github.com/stretchr/testify v1.9.0 + github.com/tidwall/btree v1.7.0 pgregory.net/rapid v1.1.0 ) @@ -16,7 +18,6 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/tidwall/btree v1.7.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sys v0.20.0 // indirect @@ -30,4 +31,5 @@ require ( replace ( cosmossdk.io/core => ../core cosmossdk.io/core/testing => ../core/testing + cosmossdk.io/schema => ../schema ) diff --git a/collections/indexing.go b/collections/indexing.go new file mode 100644 index 000000000000..e4529359090d --- /dev/null +++ b/collections/indexing.go @@ -0,0 +1,217 @@ +package collections + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/tidwall/btree" + + "cosmossdk.io/schema" + + "cosmossdk.io/collections/codec" +) + +type IndexingOptions struct { + // RetainDeletionsFor is the list of collections to retain deletions for. + RetainDeletionsFor []string +} + +func (s Schema) ModuleCodec(opts IndexingOptions) (schema.ModuleCodec, error) { + decoder := moduleDecoder{ + collectionLookup: &btree.Map[string, *collDecoder]{}, + } + + retainDeletions := make(map[string]bool) + for _, collName := range opts.RetainDeletionsFor { + retainDeletions[collName] = true + } + + var objectTypes []schema.ObjectType + for _, collName := range s.collectionsOrdered { + coll := s.collectionsByName[collName] + + // skip secondary indexes + if coll.isSecondaryIndex() { + continue + } + + ld, err := coll.logicalDecoder() + if err != nil { + return schema.ModuleCodec{}, err + } + + if !retainDeletions[coll.GetName()] { + ld.objectType.RetainDeletions = true + } + + objectTypes = append(objectTypes, ld.objectType) + decoder.collectionLookup.Set(string(coll.GetPrefix()), &collDecoder{ + Collection: coll, + logicalDecoder: ld, + }) + } + + return schema.ModuleCodec{ + Schema: schema.ModuleSchema{ + ObjectTypes: objectTypes, + }, + KVDecoder: decoder.decodeKV, + }, nil +} + +type moduleDecoder struct { + collectionLookup *btree.Map[string, *collDecoder] +} + +func (m moduleDecoder) decodeKV(update schema.KVPairUpdate) ([]schema.ObjectUpdate, error) { + key := update.Key + ks := string(key) + var cd *collDecoder + m.collectionLookup.Descend(ks, func(prefix string, cur *collDecoder) bool { + bytesPrefix := cur.GetPrefix() + if bytes.HasPrefix(key, bytesPrefix) { + cd = cur + return true + } + return false + }) + if cd == nil { + return nil, nil + } + + return cd.decodeKVPair(update) +} + +type collDecoder struct { + Collection + logicalDecoder +} + +func (c collDecoder) decodeKVPair(update schema.KVPairUpdate) ([]schema.ObjectUpdate, error) { + // strip prefix + key := update.Key + key = key[len(c.GetPrefix()):] + + k, err := c.keyDecoder(key) + if err != nil { + return []schema.ObjectUpdate{ + {TypeName: c.GetName()}, + }, err + + } + + if update.Delete { + return []schema.ObjectUpdate{ + {TypeName: c.GetName(), Key: k, Delete: true}, + }, nil + } + + v, err := c.valueDecoder(update.Value) + if err != nil { + return []schema.ObjectUpdate{ + {TypeName: c.GetName(), Key: k}, + }, err + } + + return []schema.ObjectUpdate{ + {TypeName: c.GetName(), Key: k, Value: v}, + }, nil +} + +func (c collectionImpl[K, V]) logicalDecoder() (logicalDecoder, error) { + res := logicalDecoder{} + res.objectType.Name = c.GetName() + + keyDecoder, err := KeyCodecDecoder(c.m.kc) + if err != nil { + return logicalDecoder{}, err + } + res.objectType.KeyFields = keyDecoder.Fields + res.keyDecoder = func(i []byte) (any, error) { + _, x, err := c.m.kc.Decode(i) + if err != nil { + return nil, err + } + return keyDecoder.ToSchemaType(x) + } + ensureFieldNames(c.m.kc, "key", res.objectType.KeyFields) + + valueDecoder, err := ValueCodecDecoder(c.m.vc) + if err != nil { + return logicalDecoder{}, err + } + res.objectType.ValueFields = valueDecoder.Fields + res.valueDecoder = func(i []byte) (any, error) { + x, err := c.m.vc.Decode(i) + if err != nil { + return nil, err + } + return valueDecoder.ToSchemaType(x) + } + ensureFieldNames(c.m.vc, "value", res.objectType.ValueFields) + + return res, nil +} + +func KeyCodecDecoder[K any](cdc codec.KeyCodec[K]) (codec.LogicalDecoder[K], error) { + if indexable, ok := cdc.(codec.IndexableCodec[K]); ok { + return indexable.LogicalDecoder() + } else { + return FallbackDecoder[K](), nil + } +} + +func ValueCodecDecoder[K any](cdc codec.ValueCodec[K]) (codec.LogicalDecoder[K], error) { + if indexable, ok := cdc.(codec.IndexableCodec[K]); ok { + return indexable.LogicalDecoder() + } else { + return FallbackDecoder[K](), nil + } +} + +func FallbackDecoder[T any]() codec.LogicalDecoder[T] { + var t T + kind := schema.KindForGoValue(t) + if err := kind.Validate(); err == nil { + return codec.LogicalDecoder[T]{ + Fields: []schema.Field{{Kind: kind}}, + ToSchemaType: func(t T) (any, error) { + return t, nil + }, + } + } else { + return codec.LogicalDecoder[T]{ + Fields: []schema.Field{{Kind: schema.JSONKind}}, + ToSchemaType: func(t T) (any, error) { + bz, err := json.Marshal(t) + return json.RawMessage(bz), err + }, + } + } +} + +func ensureFieldNames(x any, defaultName string, cols []schema.Field) { + var names []string = nil + if hasName, ok := x.(interface{ Name() string }); ok { + name := hasName.Name() + if name != "" { + names = strings.Split(hasName.Name(), ",") + } + } + for i, col := range cols { + if names != nil && i < len(names) { + col.Name = names[i] + } else { + if col.Name == "" { + if i == 0 && len(cols) == 1 { + col.Name = defaultName + } else { + col.Name = fmt.Sprintf("%s%d", defaultName, i+1) + } + } + } + cols[i] = col + } +} diff --git a/collections/map.go b/collections/map.go index 0b9b247aa27a..dec5bd19ebfa 100644 --- a/collections/map.go +++ b/collections/map.go @@ -17,9 +17,10 @@ type Map[K, V any] struct { vc codec.ValueCodec[V] // store accessor - sa func(context.Context) store.KVStore - prefix []byte - name string + sa func(context.Context) store.KVStore + prefix []byte + name string + isSecondaryIndex bool } // NewMap returns a Map given a StoreKey, a Prefix, human-readable name and the relative value and key encoders. diff --git a/collections/naming_test.go b/collections/naming_test.go new file mode 100644 index 000000000000..48d05d8d7670 --- /dev/null +++ b/collections/naming_test.go @@ -0,0 +1,30 @@ +package collections + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNaming(t *testing.T) { + require.Equal(t, "u16", Uint16Key.WithName("u16").Name()) + require.Equal(t, "u32", Uint32Key.WithName("u32").Name()) + require.Equal(t, "u64", Uint64Key.WithName("u64").Name()) + require.Equal(t, "i32", Int32Key.WithName("i32").Name()) + require.Equal(t, "i64", Int64Key.WithName("i64").Name()) + require.Equal(t, "str", StringKey.WithName("str").Name()) + require.Equal(t, "bytes", BytesKey.WithName("bytes").Name()) + require.Equal(t, "bool", BoolKey.WithName("bool").Name()) + + require.Equal(t, "vu16", Uint16Value.WithName("vu16").Name()) + require.Equal(t, "vu32", Uint32Value.WithName("vu32").Name()) + require.Equal(t, "vu64", Uint64Value.WithName("vu64").Name()) + require.Equal(t, "vi32", Int32Value.WithName("vi32").Name()) + require.Equal(t, "vi64", Int64Value.WithName("vi64").Name()) + require.Equal(t, "vstr", StringValue.WithName("vstr").Name()) + require.Equal(t, "vbytes", BytesValue.WithName("vbytes").Name()) + require.Equal(t, "vbool", BoolValue.WithName("vbool").Name()) + + require.Equal(t, "abc,def", NamedPairKeyCodec[bool, string]("abc", BoolKey, "def", StringKey).Name()) + require.Equal(t, "abc,def,ghi", NamedTripleKeyCodec[bool, string, int32]("abc", BoolKey, "def", StringKey, "ghi", Int32Key).Name()) +} diff --git a/collections/pair.go b/collections/pair.go index f12aaac1b576..4f0f91c0d552 100644 --- a/collections/pair.go +++ b/collections/pair.go @@ -6,6 +6,7 @@ import ( "strings" "cosmossdk.io/collections/codec" + "cosmossdk.io/schema" ) // Pair defines a key composed of two keys. @@ -54,9 +55,22 @@ func PairKeyCodec[K1, K2 any](keyCodec1 codec.KeyCodec[K1], keyCodec2 codec.KeyC } } +// NamedPairKeyCodec instantiates a new KeyCodec instance that can encode the Pair, given the KeyCodec of the +// first part of the key and the KeyCodec of the second part of the key, with names assigned to each part +// which will only be used for indexing and informational purposes. +func NamedPairKeyCodec[K1, K2 any](key1Name string, keyCodec1 codec.KeyCodec[K1], key2Name string, keyCodec2 codec.KeyCodec[K2]) codec.NamedKeyCodec[Pair[K1, K2]] { + return pairKeyCodec[K1, K2]{ + key1Name: key1Name, + key2Name: key2Name, + keyCodec1: keyCodec1, + keyCodec2: keyCodec2, + } +} + type pairKeyCodec[K1, K2 any] struct { - keyCodec1 codec.KeyCodec[K1] - keyCodec2 codec.KeyCodec[K2] + key1Name, key2Name string + keyCodec1 codec.KeyCodec[K1] + keyCodec2 codec.KeyCodec[K2] } func (p pairKeyCodec[K1, K2]) KeyCodec1() codec.KeyCodec[K1] { return p.keyCodec1 } @@ -216,6 +230,69 @@ func (p pairKeyCodec[K1, K2]) DecodeJSON(b []byte) (Pair[K1, K2], error) { return Join(k1, k2), nil } +func (p pairKeyCodec[K1, K2]) Name() string { + if p.key1Name == "" || p.key2Name == "" { + return "key1,key2" + } + return fmt.Sprintf("%s,%s", p.key1Name, p.key2Name) +} + +func (p pairKeyCodec[K1, K2]) LogicalDecoder() (res codec.LogicalDecoder[Pair[K1, K2]], err error) { + dec1, err := KeyCodecDecoder(p.keyCodec1) + if err != nil { + return + } + + dec2, err := KeyCodecDecoder(p.keyCodec2) + if err != nil { + return + } + + if len(dec1.Fields) != 1 { + err = fmt.Errorf("key1 codec must have exactly one field") + return + } + if len(dec2.Fields) != 1 { + err = fmt.Errorf("key1 codec must have exactly one field") + return + } + + fields := []schema.Field{dec1.Fields[0], dec2.Fields[0]} + fields[0].Name = p.key1Name + fields[1].Name = p.key2Name + + return codec.LogicalDecoder[Pair[K1, K2]]{ + Fields: fields, + ToSchemaType: func(pair Pair[K1, K2]) (any, error) { + k1, err := dec1.ToSchemaType(*pair.key1) + if err != nil { + return nil, err + } + + k2, err := dec2.ToSchemaType(*pair.key2) + if err != nil { + return nil, err + } + + return []any{k1, k2}, nil + }, + }, nil +} + +//func (p pairKeyCodec[K1, K2]) SchemaColumns() []schema.Field { +// //var k1 K1 +// //col1, _ := extractFields(k1) +// //if len(col1) == 1 { +// // col1[0].Name = p.key1Name +// //} +// //var k2 K2 +// //col2, _ := extractFields(k2) +// //if len(col2) == 1 { +// // col2[0].Name = p.key2Name +// //} +// //return append(col1, col2...) +//} + // NewPrefixUntilPairRange defines a collection query which ranges until the provided Pair prefix. // Unstable: this API might change in the future. func NewPrefixUntilPairRange[K1, K2 any](prefix K1) *PairRange[K1, K2] { diff --git a/collections/triple.go b/collections/triple.go index 9733d9984099..25ac7f59f782 100644 --- a/collections/triple.go +++ b/collections/triple.go @@ -64,10 +64,22 @@ func TripleKeyCodec[K1, K2, K3 any](keyCodec1 codec.KeyCodec[K1], keyCodec2 code } } +func NamedTripleKeyCodec[K1, K2, K3 any](key1Name string, keyCodec1 codec.KeyCodec[K1], key2Name string, keyCodec2 codec.KeyCodec[K2], key3Name string, keyCodec3 codec.KeyCodec[K3]) codec.NamedKeyCodec[Triple[K1, K2, K3]] { + return tripleKeyCodec[K1, K2, K3]{ + key1Name: key1Name, + key2Name: key2Name, + key3Name: key3Name, + keyCodec1: keyCodec1, + keyCodec2: keyCodec2, + keyCodec3: keyCodec3, + } +} + type tripleKeyCodec[K1, K2, K3 any] struct { - keyCodec1 codec.KeyCodec[K1] - keyCodec2 codec.KeyCodec[K2] - keyCodec3 codec.KeyCodec[K3] + key1Name, key2Name, key3Name string + keyCodec1 codec.KeyCodec[K1] + keyCodec2 codec.KeyCodec[K2] + keyCodec3 codec.KeyCodec[K3] } type jsonTripleKey [3]json.RawMessage @@ -273,6 +285,43 @@ func (t tripleKeyCodec[K1, K2, K3]) SizeNonTerminal(key Triple[K1, K2, K3]) int return size } +func (t tripleKeyCodec[K1, K2, K3]) Name() string { + if t.key1Name == "" || t.key2Name == "" || t.key3Name == "" { + return "key1,key2,key3" + } + return fmt.Sprintf("%s,%s,%s", t.key1Name, t.key2Name, t.key3Name) +} + +//func (p tripleKeyCodec[K1, K2, K3]) SchemaColumns() []indexerbase.Column { +// var k1 K1 +// col1, _ := extractFields(k1) +// if len(col1) == 1 { +// col1[0].Name = p.key1Name +// } +// var k2 K2 +// col2, _ := extractFields(k2) +// if len(col2) == 1 { +// col2[0].Name = p.key2Name +// } +// var k3 K3 +// col3, _ := extractFields(k3) +// if len(col3) == 1 { +// col3[0].Name = p.key3Name +// +// } +// cols := append(col1, col2...) +// cols = append(cols, col3...) +// return cols +//} +// +//func (p tripleKeyCodec[K1, K2, K3]) DecodeIndexable(buffer []byte) (any, error) { +// _, x, err := p.Decode(buffer) +// if err != nil { +// return nil, err +// } +// return []any{x.K1(), x.K2(), x.K3()}, nil +//} + // NewPrefixUntilTripleRange defines a collection query which ranges until the provided Pair prefix. // Unstable: this API might change in the future. func NewPrefixUntilTripleRange[K1, K2, K3 any](k1 K1) Ranger[Triple[K1, K2, K3]] { diff --git a/go.mod b/go.mod index 1e61e06ced33..d069025c157c 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/log v1.3.1 cosmossdk.io/math v1.3.0 + cosmossdk.io/schema v0.0.0 cosmossdk.io/store v1.1.1-0.20240418092142-896cdf1971bc cosmossdk.io/x/auth v0.0.0-00010101000000-000000000000 cosmossdk.io/x/bank v0.0.0-20240226161501-23359a0b6d91 @@ -190,6 +191,7 @@ replace ( cosmossdk.io/core/testing => ./core/testing cosmossdk.io/depinject => ./depinject cosmossdk.io/log => ./log + cosmossdk.io/schema => ./schema cosmossdk.io/store => ./store cosmossdk.io/x/accounts => ./x/accounts cosmossdk.io/x/auth => ./x/auth diff --git a/indexer/postgres/base.sql b/indexer/postgres/base.sql new file mode 100644 index 000000000000..b73d75140183 --- /dev/null +++ b/indexer/postgres/base.sql @@ -0,0 +1,33 @@ +CREATE SCHEMA IF NOT EXISTS indexer; + +CREATE TABLE IF NOT EXISTS indexer.indexed_modules +( + module_name TEXT NOT NULL PRIMARY KEY +); + +CREATE TABLE IF NOT EXISTS block +( + block_number BIGINT NOT NULL PRIMARY KEY, + header JSONB NULL +); + +CREATE TABLE IF NOT EXISTS tx +( + id BIGSERIAL PRIMARY KEY, + block_number BIGINT NOT NULL REFERENCES block (block_number), + tx_index BIGINT NOT NULL, + data JSONB NOT NULL +); + +CREATE TABLE IF NOT EXISTS event +( + id BIGSERIAL PRIMARY KEY, + block_number BIGINT NOT NULL REFERENCES block (block_number), + tx_index BIGINT NOT NULL REFERENCES tx (tx_index), + msg_idx BIGINT NOT NULL, + event_idx BIGINT NOT NULL, + type TEXT NOT NULL, + data JSONB NOT NULL +); + + diff --git a/indexer/postgres/column.go b/indexer/postgres/column.go new file mode 100644 index 000000000000..05ec015812de --- /dev/null +++ b/indexer/postgres/column.go @@ -0,0 +1,116 @@ +package postgres + +import ( + "fmt" + "io" + + "cosmossdk.io/schema" +) + +func (tm *TableManager) createColumnDef(writer io.Writer, field schema.Field) error { + _, err := fmt.Fprintf(writer, "%q ", field.Name) + if err != nil { + return err + } + + simple := simpleColumnType(field.Kind) + if simple != "" { + _, err = fmt.Fprintf(writer, "%s", simple) + if err != nil { + return err + } + + return writeNullability(writer, field.Nullable) + } else { + switch field.Kind { + case schema.EnumKind: + _, err = fmt.Fprintf(writer, "%q", enumTypeName(tm.moduleName, field.EnumDefinition)) + if err != nil { + return err + } + case schema.Bech32AddressKind: + _, err = fmt.Fprintf(writer, "TEXT") + if err != nil { + return err + } + + case schema.TimeKind: + nanosCol := fmt.Sprintf("%s_nanos", field.Name) + // TODO: retain at least microseconds in the timestamp + _, err = fmt.Fprintf(writer, "TIMESTAMPTZ GENERATED ALWAYS AS (to_timestamp(%q / 1000000000)) STORED,\n\t", nanosCol) + if err != nil { + return err + } + + _, err = fmt.Fprintf(writer, `%q BIGINT`, nanosCol) + if err != nil { + return err + } + default: + return fmt.Errorf("unexpected kind: %v, this should have been handled earlier", field.Kind) + } + + return writeNullability(writer, field.Nullable) + } +} + +func writeNullability(writer io.Writer, nullable bool) error { + if nullable { + _, err := fmt.Fprintf(writer, " NULL,\n\t") + return err + } else { + _, err := fmt.Fprintf(writer, " NOT NULL,\n\t") + return err + } +} + +func simpleColumnType(kind schema.Kind) string { + switch kind { + case schema.StringKind: + return "TEXT" + case schema.BoolKind: + return "BOOLEAN" + case schema.BytesKind: + return "BYTEA" + case schema.Int8Kind: + return "SMALLINT" + case schema.Int16Kind: + return "SMALLINT" + case schema.Int32Kind: + return "INTEGER" + case schema.Int64Kind: + return "BIGINT" + case schema.Uint8Kind: + return "SMALLINT" + case schema.Uint16Kind: + return "INTEGER" + case schema.Uint32Kind: + return "BIGINT" + case schema.Uint64Kind: + return "NUMERIC" + case schema.IntegerStringKind: + return "NUMERIC" + case schema.DecimalStringKind: + return "NUMERIC" + case schema.Float32Kind: + return "REAL" + case schema.Float64Kind: + return "DOUBLE PRECISION" + case schema.JSONKind: + return "JSONB" + case schema.DurationKind: + // TODO: set COMMENT on field indicating nanoseconds unit + return "BIGINT" // TODO: maybe convert to postgres interval + default: + return "" + } +} + +func (tm *TableManager) updatableColumnName(field schema.Field) (name string, err error) { + name = field.Name + if field.Kind == schema.TimeKind { + name = fmt.Sprintf("%s_nanos", name) + } + name = fmt.Sprintf("%q", name) + return +} diff --git a/indexer/postgres/count.go b/indexer/postgres/count.go new file mode 100644 index 000000000000..d5b4c8eb5b08 --- /dev/null +++ b/indexer/postgres/count.go @@ -0,0 +1,14 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" +) + +func (tm *TableManager) Count(ctx context.Context, tx *sql.Tx) (int, error) { + row := tx.QueryRowContext(ctx, fmt.Sprintf("SELECT COUNT(*) FROM %q;", tm.TableName())) + var count int + err := row.Scan(&count) + return count, err +} diff --git a/indexer/postgres/create_table.go b/indexer/postgres/create_table.go new file mode 100644 index 000000000000..477548d84347 --- /dev/null +++ b/indexer/postgres/create_table.go @@ -0,0 +1,106 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" + "io" + "strings" +) + +func (tm *TableManager) CreateTable(ctx context.Context, tx *sql.Tx) error { + buf := new(strings.Builder) + err := tm.CreateTableSql(buf) + if err != nil { + return err + } + + sqlStr := buf.String() + tm.options.Logger.Debug("Creating table", "table", tm.TableName(), "sql", sqlStr) + _, err = tx.ExecContext(ctx, sqlStr) + return err +} + +// CreateTableSql generates a CREATE TABLE statement for the object type. +func (tm *TableManager) CreateTableSql(writer io.Writer) error { + _, err := fmt.Fprintf(writer, "CREATE TABLE IF NOT EXISTS %q (", tm.TableName()) + if err != nil { + return err + } + isSingleton := false + if len(tm.typ.KeyFields) == 0 { + isSingleton = true + _, err = fmt.Fprintf(writer, "_id INTEGER NOT NULL CHECK (_id = 1),\n\t") + } else { + for _, field := range tm.typ.KeyFields { + err = tm.createColumnDef(writer, field) + if err != nil { + return err + } + } + } + + for _, field := range tm.typ.ValueFields { + err = tm.createColumnDef(writer, field) + if err != nil { + return err + } + } + + // add _deleted column when we have RetainDeletions set and enabled + // NOTE: needs more design + if tm.options.RetainDeletions && tm.typ.RetainDeletions { + _, err = fmt.Fprintf(writer, "_deleted BOOLEAN NOT NULL DEFAULT FALSE,\n\t") + if err != nil { + return err + } + } + + var pKeys []string + if !isSingleton { + for _, field := range tm.typ.KeyFields { + name, err := tm.updatableColumnName(field) + if err != nil { + return err + } + + pKeys = append(pKeys, name) + } + } else { + pKeys = []string{"_id"} + } + + _, err = fmt.Fprintf(writer, "PRIMARY KEY (%s)", strings.Join(pKeys, ", ")) + if err != nil { + return err + } + + // TODO: we need test data to not generate constraint failures to safely enable this + //for _, uniq := range tm.typ.UniqueConstraints { + // cols := make([]string, len(uniq.FieldNames)) + // for i, name := range uniq.FieldNames { + // field, ok := tm.allFields[name] + // if !ok { + // return fmt.Errorf("unknown field %q in unique constraint", name) + // } + // + // cols[i], err = tm.updatableColumnName(field) + // if err != nil { + // return err + // } + // } + // + // _, err = fmt.Fprintf(writer, ",\n\tUNIQUE NULLS NOT DISTINCT (%s)", strings.Join(cols, ", ")) + //} + + _, err = fmt.Fprintf(writer, ` +); + +GRANT SELECT ON TABLE %q TO PUBLIC; +`, tm.TableName()) + if err != nil { + return err + } + + return nil +} diff --git a/indexer/postgres/delete.go b/indexer/postgres/delete.go new file mode 100644 index 000000000000..d6ad911d42bb --- /dev/null +++ b/indexer/postgres/delete.go @@ -0,0 +1,58 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" + "io" + "strings" +) + +func (tm *TableManager) Delete(ctx context.Context, tx *sql.Tx, key interface{}) error { + buf := new(strings.Builder) + var params []interface{} + var err error + if tm.options.RetainDeletions && tm.typ.RetainDeletions { + params, err = tm.RetainDeleteSqlAndParams(buf, key) + } else { + params, err = tm.DeleteSqlAndParams(buf, key) + } + if err != nil { + return err + } + + sqlStr := buf.String() + tm.options.Logger.Debug("Delete", "sql", sqlStr, "params", params) + _, err = tx.ExecContext(ctx, sqlStr, params...) + return err +} + +func (tm *TableManager) DeleteSqlAndParams(w io.Writer, key interface{}) ([]interface{}, error) { + _, err := fmt.Fprintf(w, "DELETE FROM %q", tm.TableName()) + if err != nil { + return nil, err + } + + _, keyParams, err := tm.WhereSqlAndParams(w, key, 1) + if err != nil { + return nil, err + } + + _, err = fmt.Fprintf(w, ";") + return keyParams, err +} + +func (tm *TableManager) RetainDeleteSqlAndParams(w io.Writer, key interface{}) ([]interface{}, error) { + _, err := fmt.Fprintf(w, "UPDATE %q SET _deleted = TRUE", tm.TableName()) + if err != nil { + return nil, err + } + + _, keyParams, err := tm.WhereSqlAndParams(w, key, 1) + if err != nil { + return nil, err + } + + _, err = fmt.Fprintf(w, ";") + return keyParams, err +} diff --git a/indexer/postgres/enum.go b/indexer/postgres/enum.go new file mode 100644 index 000000000000..49cbf8b740b5 --- /dev/null +++ b/indexer/postgres/enum.go @@ -0,0 +1,86 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" + "io" + "strings" + + "cosmossdk.io/schema" +) + +func (m *ModuleManager) createEnumTypesForFields(ctx context.Context, tx *sql.Tx, fields []schema.Field) error { + for _, field := range fields { + if field.Kind != schema.EnumKind { + continue + } + + if _, ok := m.definedEnums[field.EnumDefinition.Name]; ok { + // if the enum type is already defined, skip + // we assume validation already happened + continue + } + + err := m.CreateEnumType(ctx, tx, field.EnumDefinition) + if err != nil { + return err + } + + m.definedEnums[field.EnumDefinition.Name] = field.EnumDefinition + } + + return nil +} + +func enumTypeName(moduleName string, enum schema.EnumDefinition) string { + return fmt.Sprintf("%s_%s", moduleName, enum.Name) +} + +func (m *ModuleManager) CreateEnumType(ctx context.Context, tx *sql.Tx, enum schema.EnumDefinition) error { + typeName := enumTypeName(m.moduleName, enum) + row := tx.QueryRowContext(ctx, "SELECT 1 FROM pg_type WHERE typname = $1", typeName) + var res interface{} + if err := row.Scan(&res); err != nil { + if err != sql.ErrNoRows { + return fmt.Errorf("failed to check if enum type %q exists: %w", typeName, err) + } + } else { + // the enum type already exists + return nil + } + + buf := new(strings.Builder) + err := m.CreateEnumTypeSql(buf, enum) + if err != nil { + return err + } + + sqlStr := buf.String() + m.options.Logger.Debug("Creating enum type", "sql", sqlStr) + _, err = tx.ExecContext(ctx, sqlStr) + return err +} + +func (m *ModuleManager) CreateEnumTypeSql(writer io.Writer, enum schema.EnumDefinition) error { + _, err := fmt.Fprintf(writer, "CREATE TYPE %q AS ENUM (", enumTypeName(m.moduleName, enum)) + if err != nil { + return err + } + + for i, value := range enum.Values { + if i > 0 { + _, err = fmt.Fprintf(writer, ", ") + if err != nil { + return err + } + } + _, err = fmt.Fprintf(writer, "'%s'", value) + if err != nil { + return err + } + } + + _, err = fmt.Fprintf(writer, ");") + return err +} diff --git a/indexer/postgres/go.mod b/indexer/postgres/go.mod new file mode 100644 index 000000000000..dc0615cf41b6 --- /dev/null +++ b/indexer/postgres/go.mod @@ -0,0 +1,16 @@ +module cosmossdk.io/indexer/postgres + +// NOTE: we are staying on an earlier version of golang to avoid problems building +// with older codebases. +go 1.12 + +// NOTE: cosmossdk.io/schema should be the only dependency here +// so there are no problems building this with any version of the SDK. +// This module should only use the golang standard library (database/sql) +// and cosmossdk.io/indexer/base. +require cosmossdk.io/schema v0.0.0 + +// TODO: is this dependency okay? +require github.com/cosmos/btcutil v1.0.5 + +replace cosmossdk.io/schema => ../../schema diff --git a/indexer/postgres/go.sum b/indexer/postgres/go.sum new file mode 100644 index 000000000000..fa4b37f95d3b --- /dev/null +++ b/indexer/postgres/go.sum @@ -0,0 +1,2 @@ +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= diff --git a/indexer/postgres/graphql-proxy/.gitignore b/indexer/postgres/graphql-proxy/.gitignore new file mode 100644 index 000000000000..16d54bb13c8a --- /dev/null +++ b/indexer/postgres/graphql-proxy/.gitignore @@ -0,0 +1,24 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ diff --git a/indexer/postgres/graphql-proxy/README.md b/indexer/postgres/graphql-proxy/README.md new file mode 100644 index 000000000000..e34a99b446b4 --- /dev/null +++ b/indexer/postgres/graphql-proxy/README.md @@ -0,0 +1,47 @@ +# Astro Starter Kit: Minimal + +```sh +npm create astro@latest -- --template minimal +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal) +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json) + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro project, you'll see the following folders and files: + +```text +/ +├── public/ +├── src/ +│ └── pages/ +│ └── index.astro +└── package.json +``` + +Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. + +There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. + +Any static assets, like images, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/indexer/postgres/graphql-proxy/astro.config.mjs b/indexer/postgres/graphql-proxy/astro.config.mjs new file mode 100644 index 000000000000..37b2bbe8c702 --- /dev/null +++ b/indexer/postgres/graphql-proxy/astro.config.mjs @@ -0,0 +1,6 @@ +import { defineConfig } from 'astro/config'; + +// https://astro.build/config +export default defineConfig({ + output: 'server' +}); diff --git a/indexer/postgres/graphql-proxy/package.json b/indexer/postgres/graphql-proxy/package.json new file mode 100644 index 000000000000..df3349512273 --- /dev/null +++ b/indexer/postgres/graphql-proxy/package.json @@ -0,0 +1,19 @@ +{ + "name": "graphql-proxy", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/check": "^0.7.0", + "@types/pg": "^8.11.6", + "astro": "^4.11.0", + "pg": "^8.12.0", + "typescript": "^5.5.2" + } +} \ No newline at end of file diff --git a/indexer/postgres/graphql-proxy/pnpm-lock.yaml b/indexer/postgres/graphql-proxy/pnpm-lock.yaml new file mode 100644 index 000000000000..f8ed5b7be957 --- /dev/null +++ b/indexer/postgres/graphql-proxy/pnpm-lock.yaml @@ -0,0 +1,4311 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@astrojs/check': + specifier: ^0.7.0 + version: 0.7.0(typescript@5.5.2) + '@types/pg': + specifier: ^8.11.6 + version: 8.11.6 + astro: + specifier: ^4.11.0 + version: 4.11.0(@types/node@20.14.7)(typescript@5.5.2) + pg: + specifier: ^8.12.0 + version: 8.12.0 + typescript: + specifier: ^5.5.2 + version: 5.5.2 + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@astrojs/check@0.7.0': + resolution: {integrity: sha512-UTqwOeKNu9IYZmJXEeWnQuTdSd/pX58Hl4TUARsMlT97SVDL//kLBE4T/ctxRz6J573N87oE5ddtW/uOOnQTug==} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + + '@astrojs/compiler@2.8.0': + resolution: {integrity: sha512-yrpD1WRGqsJwANaDIdtHo+YVjvIOFAjC83lu5qENIgrafwZcJgSXDuwVMXOgok4tFzpeKLsFQ6c3FoUdloLWBQ==} + + '@astrojs/internal-helpers@0.4.0': + resolution: {integrity: sha512-6B13lz5n6BrbTqCTwhXjJXuR1sqiX/H6rTxzlXx+lN1NnV4jgnq/KJldCQaUWJzPL5SiWahQyinxAbxQtwgPHA==} + + '@astrojs/language-server@2.10.0': + resolution: {integrity: sha512-crHXpqYfA5qWioiuZnZFpTsNItgBlF1f0S9MzDYS7/pfCALkHNJ7K3w9U/j0uMKymsT4hC7BfMaX0DYlfdSzHg==} + hasBin: true + peerDependencies: + prettier: ^3.0.0 + prettier-plugin-astro: '>=0.11.0' + peerDependenciesMeta: + prettier: + optional: true + prettier-plugin-astro: + optional: true + + '@astrojs/markdown-remark@5.1.0': + resolution: {integrity: sha512-S6Z3K2hOB7MfjeDoHsotnP/q2UsnEDB8NlNAaCjMDsGBZfTUbWxyLW3CaphEWw08f6KLZi2ibK9yC3BaMhh2NQ==} + + '@astrojs/prism@3.1.0': + resolution: {integrity: sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==} + engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0} + + '@astrojs/telemetry@3.1.0': + resolution: {integrity: sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA==} + engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.24.7': + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.24.7': + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.24.7': + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.24.7': + resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.7': + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.7': + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.24.7': + resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.7': + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.7': + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.24.7': + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.7': + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-jsx@7.24.7': + resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.24.7': + resolution: {integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.24.7': + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.24.7': + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + + '@emmetio/abbreviation@2.3.3': + resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==} + + '@emmetio/css-abbreviation@2.1.8': + resolution: {integrity: sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==} + + '@emmetio/css-parser@0.4.0': + resolution: {integrity: sha512-z7wkxRSZgrQHXVzObGkXG+Vmj3uRlpM11oCZ9pbaz0nFejvCDmAiNDpY75+wgXOcffKpj4rzGtwGaZxfJKsJxw==} + + '@emmetio/html-matcher@1.3.0': + resolution: {integrity: sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==} + + '@emmetio/scanner@1.0.4': + resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==} + + '@emmetio/stream-reader-utils@0.1.0': + resolution: {integrity: sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==} + + '@emmetio/stream-reader@2.2.0': + resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==} + + '@emnapi/runtime@1.2.0': + resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@img/sharp-darwin-arm64@0.33.4': + resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.4': + resolution: {integrity: sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.2': + resolution: {integrity: sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==} + engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.2': + resolution: {integrity: sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==} + engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.2': + resolution: {integrity: sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.2': + resolution: {integrity: sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.2': + resolution: {integrity: sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.2': + resolution: {integrity: sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.2': + resolution: {integrity: sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.2': + resolution: {integrity: sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.4': + resolution: {integrity: sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.4': + resolution: {integrity: sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.4': + resolution: {integrity: sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==} + engines: {glibc: '>=2.31', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.4': + resolution: {integrity: sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.4': + resolution: {integrity: sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.4': + resolution: {integrity: sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.4': + resolution: {integrity: sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.33.4': + resolution: {integrity: sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.4': + resolution: {integrity: sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [win32] + + '@johnsoncodehk/vscode-html-languageservice@5.2.0-34a5462': + resolution: {integrity: sha512-etqLfpSJ5zaw76KUNF603be6d6QsiQPmaHr9FKEp4zhLZJzWCCMH6Icak7MtLUFLZLMpL761mZNImi/joBo1ZA==} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@rollup/rollup-android-arm-eabi@4.18.0': + resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.0': + resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.0': + resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.0': + resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.0': + resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.0': + resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.0': + resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.0': + resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.0': + resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.0': + resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.0': + resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.0': + resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.0': + resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.0': + resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} + cpu: [x64] + os: [win32] + + '@shikijs/core@1.9.0': + resolution: {integrity: sha512-cbSoY8P/jgGByG8UOl3jnP/CWg/Qk+1q+eAKWtcrU3pNoILF8wTsLB0jT44qUBV8Ce1SvA9uqcM9Xf+u3fJFBw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@0.7.34': + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + + '@types/nlcst@1.0.4': + resolution: {integrity: sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg==} + + '@types/node@20.14.7': + resolution: {integrity: sha512-uTr2m2IbJJucF3KUxgnGOZvYbN0QgkGyWxG6973HCpMYFy2KfcgYuIwkJQMQkt1VbBMlvWRbpshFTLxnxCZjKQ==} + + '@types/pg@8.11.6': + resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==} + + '@types/unist@2.0.10': + resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + + '@types/unist@3.0.2': + resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@volar/kit@2.2.5': + resolution: {integrity: sha512-Bmn0UCaT43xUGGRwcmFG9lKhiCCLjRT4ScSLLPn5C9ltUcSGnIFFDlbZZa1PreHYHq25/4zkXt9Ap32klAh17w==} + peerDependencies: + typescript: '*' + + '@volar/language-core@2.2.5': + resolution: {integrity: sha512-2htyAuxRrAgETmFeUhT4XLELk3LiEcqoW/B8YUXMF6BrGWLMwIR09MFaZYvrA2UhbdAeSyeQ726HaWSWkexUcQ==} + + '@volar/language-server@2.2.5': + resolution: {integrity: sha512-PV/jkUkI+m72HTXwnY7hsGqLY3VNi96ZRoWFRzVC9QG/853bixxjveXPJIiydMJ9I739lO3kcj3hnGrF5Sm+HA==} + + '@volar/language-service@2.2.5': + resolution: {integrity: sha512-a97e/0uCe+uSu23F4zvgvldqJtZe6jugQeEHWjTfhgOEO8+Be0t5CZNNVItQqmPyAsD8eElg0S/cP6uxvCmCSQ==} + + '@volar/snapshot-document@2.2.5': + resolution: {integrity: sha512-MTOvWVKxM7ugKO3Amffkv2pND03fe2JtfygYaputqjVFML7YxtTXj8SPnI2pODLeSwOKzDYL6Q8r5j6Y5AgUzQ==} + + '@volar/source-map@2.2.5': + resolution: {integrity: sha512-wrOEIiZNf4E+PWB0AxyM4tfhkfldPsb3bxg8N6FHrxJH2ohar7aGu48e98bp3pR9HUA7P/pR9VrLmkTrgCCnWQ==} + + '@volar/typescript@2.2.5': + resolution: {integrity: sha512-eSV/n75+ppfEVugMC/salZsI44nXDPAyL6+iTYCNLtiLHGJsnMv9GwiDMujrvAUj/aLQyqRJgYtXRoxop2clCw==} + + '@vscode/emmet-helper@2.9.3': + resolution: {integrity: sha512-rB39LHWWPQYYlYfpv9qCoZOVioPCftKXXqrsyqN1mTWZM6dTnONT63Db+03vgrBbHzJN45IrgS/AGxw9iiqfEw==} + + '@vscode/l10n@0.0.16': + resolution: {integrity: sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg==} + + '@vscode/l10n@0.0.18': + resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + + acorn@8.12.0: + resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + + astro@4.11.0: + resolution: {integrity: sha512-3VWxz/08sChQIX68tuE7Y769DUdjsT3Zq2/y4SkrDRlwN9IZ/aebwcRWr5a2yMSdO2vpFxtEdobq0mKnMlLErg==} + engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + + axobject-query@4.0.0: + resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.1: + resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + caniuse-lite@1.0.30001636: + resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + ci-info@4.0.0: + resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} + engines: {node: '>=8'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + + devalue@5.0.0: + resolution: {integrity: sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dset@3.1.3: + resolution: {integrity: sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==} + engines: {node: '>=4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.4.808: + resolution: {integrity: sha512-0ItWyhPYnww2VOuCGF4s1LTfbrdAV2ajy/TN+ZTuhR23AHI6rWHCrBXJ/uxoXOvRRqw8qjYVrG81HFI7x/2wdQ==} + + emmet@2.4.7: + resolution: {integrity: sha512-O5O5QNqtdlnQM2bmKHtJgyChcrFMgQuulI+WdiOw2NArzprUqqxUW6bgYtKvzKgrsYpuLWalOkdhNP+1jluhCA==} + + emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-module-lexer@1.5.3: + resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-from-html@2.0.1: + resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==} + + hast-util-from-parse5@8.0.1: + resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.0.4: + resolution: {integrity: sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==} + + hast-util-to-html@9.0.1: + resolution: {integrity: sha512-hZOofyZANbyWo+9RP75xIDV/gq+OUKx+T46IlwERnKmfpwp81XBFbT9mi26ws+SJchA4RVUQwIBJpqEOBhMzEQ==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@8.0.0: + resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.0.0: + resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} + engines: {node: '>=18'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@2.3.1: + resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + + mdast-util-find-and-replace@3.0.1: + resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + + mdast-util-from-markdown@2.0.1: + resolution: {integrity: sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==} + + mdast-util-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} + + mdast-util-gfm-footnote@2.0.0: + resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.0.0: + resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.0: + resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-core-commonmark@2.0.1: + resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} + + micromark-extension-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + + micromark-extension-gfm-footnote@2.0.0: + resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + + micromark-extension-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + + micromark-extension-gfm-table@2.0.0: + resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.0.1: + resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.0: + resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + + micromark-factory-label@2.0.0: + resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + + micromark-factory-space@2.0.0: + resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + + micromark-factory-title@2.0.0: + resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + + micromark-factory-whitespace@2.0.0: + resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + + micromark-util-character@2.1.0: + resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + + micromark-util-chunked@2.0.0: + resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + + micromark-util-classify-character@2.0.0: + resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + + micromark-util-combine-extensions@2.0.0: + resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + + micromark-util-decode-numeric-character-reference@2.0.1: + resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + + micromark-util-decode-string@2.0.0: + resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + + micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + + micromark-util-html-tag-name@2.0.0: + resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + + micromark-util-normalize-identifier@2.0.0: + resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + + micromark-util-resolve-all@2.0.0: + resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + + micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + + micromark-util-subtokenize@2.0.1: + resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} + + micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + + micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + + micromark@4.0.0: + resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nlcst-to-string@3.1.1: + resolution: {integrity: sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==} + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + ora@8.0.1: + resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==} + engines: {node: '>=18'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-queue@8.0.1: + resolution: {integrity: sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==} + engines: {node: '>=18'} + + p-timeout@6.1.2: + resolution: {integrity: sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==} + engines: {node: '>=14.16'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parse-latin@5.0.1: + resolution: {integrity: sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg==} + + parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@6.2.2: + resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + + pg-pool@3.6.2: + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + + pg@8.12.0: + resolution: {integrity: sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + + postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} + + preferred-pm@3.1.3: + resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} + engines: {node: '>=10'} + + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + rehype-parse@9.0.0: + resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-stringify@10.0.0: + resolution: {integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==} + + rehype@13.0.1: + resolution: {integrity: sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg==} + + remark-gfm@4.0.0: + resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.0: + resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==} + + remark-smartypants@2.1.0: + resolution: {integrity: sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + request-light@0.7.0: + resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + retext-latin@3.1.0: + resolution: {integrity: sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ==} + + retext-smartypants@5.2.0: + resolution: {integrity: sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw==} + + retext-stringify@3.1.0: + resolution: {integrity: sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w==} + + retext@8.1.0: + resolution: {integrity: sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q==} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.18.0: + resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.33.4: + resolution: {integrity: sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==} + engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shiki@1.9.0: + resolution: {integrity: sha512-i6//Lqgn7+7nZA0qVjoYH0085YdNk4MC+tJV4bo+HgjgRMJ0JmkLZzFAuvVioJqLkcGDK5GAMpghZEZkCnwxpQ==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.1.0: + resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} + engines: {node: '>=18'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tsconfck@3.1.0: + resolution: {integrity: sha512-CMjc5zMnyAjcS9sPLytrbFmj89st2g+JYtY/c02ug4Q+CZaAtCgbyviI0n1YvjZE/pzoc6FbNsINS13DOL1B9w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typesafe-path@0.2.2: + resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==} + + typescript-auto-import-cache@0.3.3: + resolution: {integrity: sha512-ojEC7+Ci1ij9eE6hp8Jl9VUNnsEKzztktP5gtYNRMrTmfXVwA1PITYYAkpxCvvupdSYa/Re51B6KMcv1CTZEUA==} + + typescript@5.5.2: + resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + unherit@3.0.1: + resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==} + + unified@10.1.2: + resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@5.2.1: + resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-modify-children@3.1.1: + resolution: {integrity: sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@3.0.3: + resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-children@2.0.2: + resolution: {integrity: sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==} + + unist-util-visit-parents@5.1.3: + resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@4.1.2: + resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + update-browserslist-db@1.0.16: + resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + vfile-location@5.0.2: + resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==} + + vfile-message@3.1.4: + resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@5.3.7: + resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + + vfile@6.0.1: + resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} + + vite@5.3.1: + resolution: {integrity: sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitefu@0.2.5: + resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + vite: + optional: true + + volar-service-css@0.0.45: + resolution: {integrity: sha512-f+AlUI1+kESbcZSVaNJVAnK0c/9Da5StoxzPqA5/8VqUHJWNdubWNnwG5xpFVTfgh6pgTcey3UBhBfHytFaIOg==} + peerDependencies: + '@volar/language-service': ~2.2.3 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-emmet@0.0.45: + resolution: {integrity: sha512-9nLXSDkR1vA/3fQkFEsSXAu3XovQxOpTkVG2jilQgfek/K1ZLkaA/WMhN/TtmPmQg4NxE9Ni6mA5udBQ5gVXIA==} + peerDependencies: + '@volar/language-service': ~2.2.3 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-html@0.0.45: + resolution: {integrity: sha512-tLTJqfy1v5C4nmeAsfekFIKPl4r4qDMyL0L9MWywr/EApZzPCsbeUGxCqdzxSMC2q7PMCfX2i167txDo+J0LVA==} + peerDependencies: + '@volar/language-service': ~2.2.3 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-prettier@0.0.45: + resolution: {integrity: sha512-+mBS2EsDgp/kunKEBnHvhBwIQm5v2ahw4NKpKdg4sTpXy3UxqHt+Fq/wRYQ7Z8LlNVNRVfp75ThjM+w2zaZBAw==} + peerDependencies: + '@volar/language-service': ~2.2.3 + prettier: ^2.2 || ^3.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + prettier: + optional: true + + volar-service-typescript-twoslash-queries@0.0.45: + resolution: {integrity: sha512-KrPUUvKggZgV9mrDpstCzmf20irgv0ooMv+FGDzIIQUkya+d2+nSS8Mx2h9FvsYgLccUVw5jU3Rhwhd3pv/7qg==} + peerDependencies: + '@volar/language-service': ~2.2.3 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-typescript@0.0.45: + resolution: {integrity: sha512-i/mMIIAMastJ2kgPo3qvX0Rrl7NyxhIYZ0ug/B4ambZcLPI1vzBgS2fmvyWX3jhBYHh8NmbAotFj+0Y9JtN47A==} + peerDependencies: + '@volar/language-service': ~2.2.3 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + vscode-css-languageservice@6.2.14: + resolution: {integrity: sha512-5UPQ9Y1sUTnuMyaMBpO7LrBkqjhEJb5eAwdUlDp+Uez8lry+Tspnk3+3p2qWS4LlNsr4p3v9WkZxUf1ltgFpgw==} + + vscode-html-languageservice@5.2.0: + resolution: {integrity: sha512-cdNMhyw57/SQzgUUGSIMQ66jikqEN6nBNyhx5YuOyj9310+eY9zw8Q0cXpiKzDX8aHYFewQEXRnigl06j/TVwQ==} + + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.11: + resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-nls@5.2.0: + resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} + + vscode-uri@2.1.2: + resolution: {integrity: sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==} + + vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + + which-pm@2.0.0: + resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} + engines: {node: '>=8.15'} + + which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + + zod-to-json-schema@3.23.1: + resolution: {integrity: sha512-oT9INvydob1XV0v1d2IadrR74rLtDInLvDFfAa1CG0Pmg/vxATk7I2gSelfj271mbzeM4Da0uuDQE/Nkj3DWNw==} + peerDependencies: + zod: ^3.23.3 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@astrojs/check@0.7.0(typescript@5.5.2)': + dependencies: + '@astrojs/language-server': 2.10.0(typescript@5.5.2) + chokidar: 3.6.0 + fast-glob: 3.3.2 + kleur: 4.1.5 + typescript: 5.5.2 + yargs: 17.7.2 + transitivePeerDependencies: + - prettier + - prettier-plugin-astro + + '@astrojs/compiler@2.8.0': {} + + '@astrojs/internal-helpers@0.4.0': {} + + '@astrojs/language-server@2.10.0(typescript@5.5.2)': + dependencies: + '@astrojs/compiler': 2.8.0 + '@jridgewell/sourcemap-codec': 1.4.15 + '@volar/kit': 2.2.5(typescript@5.5.2) + '@volar/language-core': 2.2.5 + '@volar/language-server': 2.2.5 + '@volar/language-service': 2.2.5 + '@volar/typescript': 2.2.5 + fast-glob: 3.3.2 + volar-service-css: 0.0.45(@volar/language-service@2.2.5) + volar-service-emmet: 0.0.45(@volar/language-service@2.2.5) + volar-service-html: 0.0.45(@volar/language-service@2.2.5) + volar-service-prettier: 0.0.45(@volar/language-service@2.2.5) + volar-service-typescript: 0.0.45(@volar/language-service@2.2.5) + volar-service-typescript-twoslash-queries: 0.0.45(@volar/language-service@2.2.5) + vscode-html-languageservice: 5.2.0 + vscode-uri: 3.0.8 + transitivePeerDependencies: + - typescript + + '@astrojs/markdown-remark@5.1.0': + dependencies: + '@astrojs/prism': 3.1.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.1 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.1.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.0 + remark-gfm: 4.0.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.0 + remark-smartypants: 2.1.0 + shiki: 1.9.0 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.1 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@3.1.0': + dependencies: + prismjs: 1.29.0 + + '@astrojs/telemetry@3.1.0': + dependencies: + ci-info: 4.0.0 + debug: 4.3.5 + dlv: 1.1.3 + dset: 3.1.3 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + + '@babel/compat-data@7.24.7': {} + + '@babel/core@7.24.7': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helpers': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + convert-source-map: 2.0.0 + debug: 4.3.5 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.24.7': + dependencies: + '@babel/types': 7.24.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-annotate-as-pure@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-compilation-targets@7.24.7': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + browserslist: 4.23.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-function-name@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/helper-hoist-variables@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.24.7': {} + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-string-parser@7.24.7': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.7': {} + + '@babel/helpers@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/template@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/traverse@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + debug: 4.3.5 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.24.7': + dependencies: + '@babel/helper-string-parser': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@emmetio/abbreviation@2.3.3': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-abbreviation@2.1.8': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-parser@0.4.0': + dependencies: + '@emmetio/stream-reader': 2.2.0 + '@emmetio/stream-reader-utils': 0.1.0 + + '@emmetio/html-matcher@1.3.0': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/scanner@1.0.4': {} + + '@emmetio/stream-reader-utils@0.1.0': {} + + '@emmetio/stream-reader@2.2.0': {} + + '@emnapi/runtime@1.2.0': + dependencies: + tslib: 2.6.3 + optional: true + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@img/sharp-darwin-arm64@0.33.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.2 + optional: true + + '@img/sharp-darwin-x64@0.33.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.2 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.2': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.2': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.2': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.2': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.2': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.2': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.2': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.2': + optional: true + + '@img/sharp-linux-arm64@0.33.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.2 + optional: true + + '@img/sharp-linux-arm@0.33.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.2 + optional: true + + '@img/sharp-linux-s390x@0.33.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.2 + optional: true + + '@img/sharp-linux-x64@0.33.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.2 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.2 + optional: true + + '@img/sharp-wasm32@0.33.4': + dependencies: + '@emnapi/runtime': 1.2.0 + optional: true + + '@img/sharp-win32-ia32@0.33.4': + optional: true + + '@img/sharp-win32-x64@0.33.4': + optional: true + + '@johnsoncodehk/vscode-html-languageservice@5.2.0-34a5462': + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.0.8 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.4.15': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@rollup/rollup-android-arm-eabi@4.18.0': + optional: true + + '@rollup/rollup-android-arm64@4.18.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.18.0': + optional: true + + '@rollup/rollup-darwin-x64@4.18.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.18.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.18.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.18.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.18.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.18.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.18.0': + optional: true + + '@shikijs/core@1.9.0': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.24.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.24.7 + + '@types/cookie@0.6.0': {} + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 0.7.34 + + '@types/estree@1.0.5': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.2 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.2 + + '@types/ms@0.7.34': {} + + '@types/nlcst@1.0.4': + dependencies: + '@types/unist': 2.0.10 + + '@types/node@20.14.7': + dependencies: + undici-types: 5.26.5 + + '@types/pg@8.11.6': + dependencies: + '@types/node': 20.14.7 + pg-protocol: 1.6.1 + pg-types: 4.0.2 + + '@types/unist@2.0.10': {} + + '@types/unist@3.0.2': {} + + '@ungap/structured-clone@1.2.0': {} + + '@volar/kit@2.2.5(typescript@5.5.2)': + dependencies: + '@volar/language-service': 2.2.5 + '@volar/typescript': 2.2.5 + typesafe-path: 0.2.2 + typescript: 5.5.2 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + + '@volar/language-core@2.2.5': + dependencies: + '@volar/source-map': 2.2.5 + + '@volar/language-server@2.2.5': + dependencies: + '@volar/language-core': 2.2.5 + '@volar/language-service': 2.2.5 + '@volar/snapshot-document': 2.2.5 + '@volar/typescript': 2.2.5 + '@vscode/l10n': 0.0.16 + path-browserify: 1.0.1 + request-light: 0.7.0 + vscode-languageserver: 9.0.1 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + + '@volar/language-service@2.2.5': + dependencies: + '@volar/language-core': 2.2.5 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + + '@volar/snapshot-document@2.2.5': + dependencies: + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.11 + + '@volar/source-map@2.2.5': + dependencies: + muggle-string: 0.4.1 + + '@volar/typescript@2.2.5': + dependencies: + '@volar/language-core': 2.2.5 + path-browserify: 1.0.1 + + '@vscode/emmet-helper@2.9.3': + dependencies: + emmet: 2.4.7 + jsonc-parser: 2.3.1 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 + vscode-uri: 2.1.2 + + '@vscode/l10n@0.0.16': {} + + '@vscode/l10n@0.0.18': {} + + acorn@8.12.0: {} + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + array-iterate@2.0.1: {} + + astro@4.11.0(@types/node@20.14.7)(typescript@5.5.2): + dependencies: + '@astrojs/compiler': 2.8.0 + '@astrojs/internal-helpers': 0.4.0 + '@astrojs/markdown-remark': 5.1.0 + '@astrojs/telemetry': 3.1.0 + '@babel/core': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__core': 7.20.5 + '@types/cookie': 0.6.0 + acorn: 8.12.0 + aria-query: 5.3.0 + axobject-query: 4.0.0 + boxen: 7.1.1 + chokidar: 3.6.0 + ci-info: 4.0.0 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 0.6.0 + cssesc: 3.0.0 + debug: 4.3.5 + deterministic-object-hash: 2.0.2 + devalue: 5.0.0 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.3 + es-module-lexer: 1.5.3 + esbuild: 0.21.5 + estree-walker: 3.0.3 + execa: 8.0.1 + fast-glob: 3.3.2 + flattie: 1.1.1 + github-slugger: 2.0.0 + gray-matter: 4.0.3 + html-escaper: 3.0.3 + http-cache-semantics: 4.1.1 + js-yaml: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.10 + mrmime: 2.0.0 + ora: 8.0.1 + p-limit: 5.0.0 + p-queue: 8.0.1 + path-to-regexp: 6.2.2 + preferred-pm: 3.1.3 + prompts: 2.4.2 + rehype: 13.0.1 + resolve: 1.22.8 + semver: 7.6.2 + shiki: 1.9.0 + string-width: 7.1.0 + strip-ansi: 7.1.0 + tsconfck: 3.1.0(typescript@5.5.2) + unist-util-visit: 5.0.0 + vfile: 6.0.1 + vite: 5.3.1(@types/node@20.14.7) + vitefu: 0.2.5(vite@5.3.1(@types/node@20.14.7)) + which-pm: 2.2.0 + yargs-parser: 21.1.1 + zod: 3.23.8 + zod-to-json-schema: 3.23.1(zod@3.23.8) + optionalDependencies: + sharp: 0.33.4 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + - typescript + + axobject-query@4.0.0: + dependencies: + dequal: 2.0.3 + + bail@2.0.2: {} + + base-64@1.0.0: {} + + binary-extensions@2.3.0: {} + + boxen@7.1.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.3.0 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.23.1: + dependencies: + caniuse-lite: 1.0.30001636 + electron-to-chromium: 1.4.808 + node-releases: 2.0.14 + update-browserslist-db: 1.0.16(browserslist@4.23.1) + + camelcase@7.0.1: {} + + caniuse-lite@1.0.30001636: {} + + ccount@2.0.1: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@5.3.0: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + ci-info@4.0.0: {} + + cli-boxes@3.0.0: {} + + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + + cli-spinners@2.9.2: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clsx@2.1.1: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + optional: true + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + optional: true + + comma-separated-tokens@2.0.3: {} + + common-ancestor-path@1.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@0.6.0: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + debug@4.3.5: + dependencies: + ms: 2.1.2 + + decode-named-character-reference@1.0.2: + dependencies: + character-entities: 2.0.2 + + dequal@2.0.3: {} + + detect-libc@2.0.3: + optional: true + + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + + devalue@5.0.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@5.2.0: {} + + dlv@1.1.3: {} + + dset@3.1.3: {} + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.4.808: {} + + emmet@2.4.7: + dependencies: + '@emmetio/abbreviation': 2.3.3 + '@emmetio/css-abbreviation': 2.1.8 + + emoji-regex@10.3.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@4.5.0: {} + + es-module-lexer@1.5.3: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.1.2: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@5.0.0: {} + + esprima@4.0.1: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 + + eventemitter3@5.0.1: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend@3.0.2: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-yarn-workspace-root2@1.2.16: + dependencies: + micromatch: 4.0.7 + pkg-dir: 4.2.0 + + flattie@1.1.1: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.2.0: {} + + get-stream@8.0.1: {} + + github-slugger@2.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + globals@11.12.0: {} + + graceful-fs@4.2.11: {} + + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + + has-flag@3.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-from-html@2.0.1: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.1 + parse5: 7.1.2 + vfile: 6.0.1 + vfile-message: 4.0.2 + + hast-util-from-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.2 + devlop: 1.1.0 + hastscript: 8.0.0 + property-information: 6.5.0 + vfile: 6.0.1 + vfile-location: 5.0.2 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.0.4: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.2 + '@ungap/structured-clone': 1.2.0 + hast-util-from-parse5: 8.0.1 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.1.2 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-html@9.0.1: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.2 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-raw: 9.0.4 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.2 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + http-cache-semantics@4.1.1: {} + + human-signals@5.0.0: {} + + import-meta-resolve@4.1.0: {} + + is-arrayish@0.3.2: + optional: true + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-buffer@2.0.5: {} + + is-core-module@2.14.0: + dependencies: + hasown: 2.0.2 + + is-docker@3.0.0: {} + + is-extendable@0.1.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-interactive@2.0.0: {} + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-stream@3.0.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.0.0: {} + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isexe@2.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@2.5.2: {} + + json5@2.2.3: {} + + jsonc-parser@2.3.1: {} + + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + load-yaml-file@0.2.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + + longest-streak@3.1.0: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.10: + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + + markdown-table@3.0.3: {} + + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.2 + unist-util-visit: 5.0.0 + + mdast-util-find-and-replace@3.0.1: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.2 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-decode-string: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.1 + micromark-util-character: 2.1.0 + + mdast-util-gfm-footnote@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.1 + mdast-util-to-markdown: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.1 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.3 + mdast-util-from-markdown: 2.0.1 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.1 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.1 + mdast-util-gfm-autolink-literal: 2.0.0 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.0 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + + mdast-util-to-markdown@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.2 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-decode-string: 2.0.0 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.1: + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.0 + micromark-factory-label: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-factory-title: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-html-tag-name: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-subtokenize: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-gfm-autolink-literal@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-gfm-footnote@2.0.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-gfm-strikethrough@2.0.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-gfm-table@2.0.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.0 + + micromark-extension-gfm-task-list-item@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.0.0 + micromark-extension-gfm-footnote: 2.0.0 + micromark-extension-gfm-strikethrough: 2.0.0 + micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.0.1 + micromark-util-combine-extensions: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-destination@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-label@2.0.0: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-space@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-types: 2.0.0 + + micromark-factory-title@2.0.0: + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-factory-whitespace@2.0.0: + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-character@2.1.0: + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-chunked@2.0.0: + dependencies: + micromark-util-symbol: 2.0.0 + + micromark-util-classify-character@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-combine-extensions@2.0.0: + dependencies: + micromark-util-chunked: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-decode-numeric-character-reference@2.0.1: + dependencies: + micromark-util-symbol: 2.0.0 + + micromark-util-decode-string@2.0.0: + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-symbol: 2.0.0 + + micromark-util-encode@2.0.0: {} + + micromark-util-html-tag-name@2.0.0: {} + + micromark-util-normalize-identifier@2.0.0: + dependencies: + micromark-util-symbol: 2.0.0 + + micromark-util-resolve-all@2.0.0: + dependencies: + micromark-util-types: 2.0.0 + + micromark-util-sanitize-uri@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-encode: 2.0.0 + micromark-util-symbol: 2.0.0 + + micromark-util-subtokenize@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-symbol@2.0.0: {} + + micromark-util-types@2.0.0: {} + + micromark@4.0.0: + dependencies: + '@types/debug': 4.1.12 + debug: 4.3.5 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-subtokenize: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mrmime@2.0.0: {} + + ms@2.1.2: {} + + muggle-string@0.4.1: {} + + nanoid@3.3.7: {} + + nlcst-to-string@3.1.1: + dependencies: + '@types/nlcst': 1.0.4 + + node-releases@2.0.14: {} + + normalize-path@3.0.0: {} + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + obuf@1.1.2: {} + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + ora@8.0.1: + dependencies: + chalk: 5.3.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.0.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.1.0 + strip-ansi: 7.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@5.0.0: + dependencies: + yocto-queue: 1.0.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-queue@8.0.1: + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.2 + + p-timeout@6.1.2: {} + + p-try@2.2.0: {} + + parse-latin@5.0.1: + dependencies: + nlcst-to-string: 3.1.1 + unist-util-modify-children: 3.1.1 + unist-util-visit-children: 2.0.2 + + parse5@7.1.2: + dependencies: + entities: 4.5.0 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-to-regexp@6.2.2: {} + + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.6.4: {} + + pg-int8@1.0.1: {} + + pg-numeric@1.0.2: {} + + pg-pool@3.6.2(pg@8.12.0): + dependencies: + pg: 8.12.0 + + pg-protocol@1.6.1: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg-types@4.0.2: + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + + pg@8.12.0: + dependencies: + pg-connection-string: 2.6.4 + pg-pool: 3.6.2(pg@8.12.0) + pg-protocol: 1.6.1 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pify@4.0.1: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + postcss@8.4.38: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + postgres-array@2.0.0: {} + + postgres-array@3.0.2: {} + + postgres-bytea@1.0.0: {} + + postgres-bytea@3.0.0: + dependencies: + obuf: 1.1.2 + + postgres-date@1.0.7: {} + + postgres-date@2.1.0: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres-interval@3.0.0: {} + + postgres-range@1.1.4: {} + + preferred-pm@3.1.3: + dependencies: + find-up: 5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: 4.0.0 + which-pm: 2.0.0 + + prismjs@1.29.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + property-information@6.5.0: {} + + queue-microtask@1.2.3: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + rehype-parse@9.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.1 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.0.4 + vfile: 6.0.1 + + rehype-stringify@10.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.1 + unified: 11.0.5 + + rehype@13.0.1: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.0 + rehype-stringify: 10.0.0 + unified: 11.0.5 + + remark-gfm@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.0.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.1 + micromark-util-types: 2.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.1 + + remark-smartypants@2.1.0: + dependencies: + retext: 8.1.0 + retext-smartypants: 5.2.0 + unist-util-visit: 5.0.0 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.0 + unified: 11.0.5 + + request-light@0.7.0: {} + + require-directory@2.1.1: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.14.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + retext-latin@3.1.0: + dependencies: + '@types/nlcst': 1.0.4 + parse-latin: 5.0.1 + unherit: 3.0.1 + unified: 10.1.2 + + retext-smartypants@5.2.0: + dependencies: + '@types/nlcst': 1.0.4 + nlcst-to-string: 3.1.1 + unified: 10.1.2 + unist-util-visit: 4.1.2 + + retext-stringify@3.1.0: + dependencies: + '@types/nlcst': 1.0.4 + nlcst-to-string: 3.1.1 + unified: 10.1.2 + + retext@8.1.0: + dependencies: + '@types/nlcst': 1.0.4 + retext-latin: 3.1.0 + retext-stringify: 3.1.0 + unified: 10.1.2 + + reusify@1.0.4: {} + + rollup@4.18.0: + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.18.0 + '@rollup/rollup-android-arm64': 4.18.0 + '@rollup/rollup-darwin-arm64': 4.18.0 + '@rollup/rollup-darwin-x64': 4.18.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 + '@rollup/rollup-linux-arm-musleabihf': 4.18.0 + '@rollup/rollup-linux-arm64-gnu': 4.18.0 + '@rollup/rollup-linux-arm64-musl': 4.18.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 + '@rollup/rollup-linux-riscv64-gnu': 4.18.0 + '@rollup/rollup-linux-s390x-gnu': 4.18.0 + '@rollup/rollup-linux-x64-gnu': 4.18.0 + '@rollup/rollup-linux-x64-musl': 4.18.0 + '@rollup/rollup-win32-arm64-msvc': 4.18.0 + '@rollup/rollup-win32-ia32-msvc': 4.18.0 + '@rollup/rollup-win32-x64-msvc': 4.18.0 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + + semver@6.3.1: {} + + semver@7.6.2: {} + + sharp@0.33.4: + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + semver: 7.6.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.4 + '@img/sharp-darwin-x64': 0.33.4 + '@img/sharp-libvips-darwin-arm64': 1.0.2 + '@img/sharp-libvips-darwin-x64': 1.0.2 + '@img/sharp-libvips-linux-arm': 1.0.2 + '@img/sharp-libvips-linux-arm64': 1.0.2 + '@img/sharp-libvips-linux-s390x': 1.0.2 + '@img/sharp-libvips-linux-x64': 1.0.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 + '@img/sharp-libvips-linuxmusl-x64': 1.0.2 + '@img/sharp-linux-arm': 0.33.4 + '@img/sharp-linux-arm64': 0.33.4 + '@img/sharp-linux-s390x': 0.33.4 + '@img/sharp-linux-x64': 0.33.4 + '@img/sharp-linuxmusl-arm64': 0.33.4 + '@img/sharp-linuxmusl-x64': 0.33.4 + '@img/sharp-wasm32': 0.33.4 + '@img/sharp-win32-ia32': 0.33.4 + '@img/sharp-win32-x64': 0.33.4 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@1.9.0: + dependencies: + '@shikijs/core': 1.9.0 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + optional: true + + sisteransi@1.0.5: {} + + source-map-js@1.2.0: {} + + space-separated-tokens@2.0.2: {} + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + stdin-discarder@0.2.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.1.0: + dependencies: + emoji-regex: 10.3.0 + get-east-asian-width: 1.2.0 + strip-ansi: 7.1.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + strip-bom-string@1.0.0: {} + + strip-bom@3.0.0: {} + + strip-final-newline@3.0.0: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tsconfck@3.1.0(typescript@5.5.2): + optionalDependencies: + typescript: 5.5.2 + + tslib@2.6.3: + optional: true + + type-fest@2.19.0: {} + + typesafe-path@0.2.2: {} + + typescript-auto-import-cache@0.3.3: + dependencies: + semver: 7.6.2 + + typescript@5.5.2: {} + + undici-types@5.26.5: {} + + unherit@3.0.1: {} + + unified@10.1.2: + dependencies: + '@types/unist': 2.0.10 + bail: 2.0.2 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 5.3.7 + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.2 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.1 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + + unist-util-is@5.2.1: + dependencies: + '@types/unist': 2.0.10 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.2 + + unist-util-modify-children@3.1.1: + dependencies: + '@types/unist': 2.0.10 + array-iterate: 2.0.1 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.2 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.2 + unist-util-visit: 5.0.0 + + unist-util-stringify-position@3.0.3: + dependencies: + '@types/unist': 2.0.10 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.2 + + unist-util-visit-children@2.0.2: + dependencies: + '@types/unist': 2.0.10 + + unist-util-visit-parents@5.1.3: + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + + unist-util-visit@4.1.2: + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + update-browserslist-db@1.0.16(browserslist@4.23.1): + dependencies: + browserslist: 4.23.1 + escalade: 3.1.2 + picocolors: 1.0.1 + + vfile-location@5.0.2: + dependencies: + '@types/unist': 3.0.2 + vfile: 6.0.1 + + vfile-message@3.1.4: + dependencies: + '@types/unist': 2.0.10 + unist-util-stringify-position: 3.0.3 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.2 + unist-util-stringify-position: 4.0.0 + + vfile@5.3.7: + dependencies: + '@types/unist': 2.0.10 + is-buffer: 2.0.5 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + + vfile@6.0.1: + dependencies: + '@types/unist': 3.0.2 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + + vite@5.3.1(@types/node@20.14.7): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.38 + rollup: 4.18.0 + optionalDependencies: + '@types/node': 20.14.7 + fsevents: 2.3.3 + + vitefu@0.2.5(vite@5.3.1(@types/node@20.14.7)): + optionalDependencies: + vite: 5.3.1(@types/node@20.14.7) + + volar-service-css@0.0.45(@volar/language-service@2.2.5): + dependencies: + vscode-css-languageservice: 6.2.14 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + optionalDependencies: + '@volar/language-service': 2.2.5 + + volar-service-emmet@0.0.45(@volar/language-service@2.2.5): + dependencies: + '@emmetio/css-parser': 0.4.0 + '@emmetio/html-matcher': 1.3.0 + '@vscode/emmet-helper': 2.9.3 + optionalDependencies: + '@volar/language-service': 2.2.5 + + volar-service-html@0.0.45(@volar/language-service@2.2.5): + dependencies: + vscode-html-languageservice: '@johnsoncodehk/vscode-html-languageservice@5.2.0-34a5462' + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + optionalDependencies: + '@volar/language-service': 2.2.5 + + volar-service-prettier@0.0.45(@volar/language-service@2.2.5): + dependencies: + vscode-uri: 3.0.8 + optionalDependencies: + '@volar/language-service': 2.2.5 + + volar-service-typescript-twoslash-queries@0.0.45(@volar/language-service@2.2.5): + optionalDependencies: + '@volar/language-service': 2.2.5 + + volar-service-typescript@0.0.45(@volar/language-service@2.2.5): + dependencies: + path-browserify: 1.0.1 + semver: 7.6.2 + typescript-auto-import-cache: 0.3.3 + vscode-languageserver-textdocument: 1.0.11 + vscode-nls: 5.2.0 + optionalDependencies: + '@volar/language-service': 2.2.5 + + vscode-css-languageservice@6.2.14: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.0.8 + + vscode-html-languageservice@5.2.0: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.0.8 + + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.11: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-nls@5.2.0: {} + + vscode-uri@2.1.2: {} + + vscode-uri@3.0.8: {} + + web-namespaces@2.0.1: {} + + which-pm-runs@1.1.0: {} + + which-pm@2.0.0: + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + + which-pm@2.2.0: + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + widest-line@4.0.1: + dependencies: + string-width: 5.1.2 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + yocto-queue@1.0.0: {} + + zod-to-json-schema@3.23.1(zod@3.23.8): + dependencies: + zod: 3.23.8 + + zod@3.23.8: {} + + zwitch@2.0.4: {} diff --git a/indexer/postgres/graphql-proxy/public/favicon.svg b/indexer/postgres/graphql-proxy/public/favicon.svg new file mode 100644 index 000000000000..f157bd1c5e28 --- /dev/null +++ b/indexer/postgres/graphql-proxy/public/favicon.svg @@ -0,0 +1,9 @@ + + + + diff --git a/indexer/postgres/graphql-proxy/src/env.d.ts b/indexer/postgres/graphql-proxy/src/env.d.ts new file mode 100644 index 000000000000..f964fe0cffd8 --- /dev/null +++ b/indexer/postgres/graphql-proxy/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/indexer/postgres/graphql-proxy/src/pages/graphiql.astro b/indexer/postgres/graphql-proxy/src/pages/graphiql.astro new file mode 100644 index 000000000000..02674b666423 --- /dev/null +++ b/indexer/postgres/graphql-proxy/src/pages/graphiql.astro @@ -0,0 +1,81 @@ + + + + + GraphiQL + + + + + + + + + + + + + + +
Loading...
+ + + \ No newline at end of file diff --git a/indexer/postgres/graphql-proxy/src/pages/graphql.ts b/indexer/postgres/graphql-proxy/src/pages/graphql.ts new file mode 100644 index 000000000000..e9791859d4ef --- /dev/null +++ b/indexer/postgres/graphql-proxy/src/pages/graphql.ts @@ -0,0 +1,47 @@ +import pg from 'pg' + +const {Client} = pg +const client = new Client({ + connectionString: import.meta.env.DATABASE_URL, +}) +await client.connect() + + +export async function GET({params, request}) { + const {query, variables, operationName} = params; + + return graphql(query, variables, operationName); +} + +export async function POST({params, request}) { + const {query, operationName, variables} = await request.json(); + return graphql(query, variables, operationName); +} + +async function graphql(query, variables, operationName) { + try { + const res = await client.query( + 'select graphql.resolve($1, $2, $3);', + [query, variables, operationName] + ); + + return new Response( + JSON.stringify(res.rows[0].resolve), + { + headers: { + 'Content-Type': 'application/json', + } + } + ) + } catch (e) { + return new Response( + JSON.stringify({errors: [e.message]}), + { + status: 500, + headers: { + 'Content-Type': 'application/json', + } + } + ) + } +} diff --git a/indexer/postgres/graphql-proxy/src/pages/index.astro b/indexer/postgres/graphql-proxy/src/pages/index.astro new file mode 100644 index 000000000000..240be0a56aca --- /dev/null +++ b/indexer/postgres/graphql-proxy/src/pages/index.astro @@ -0,0 +1,11 @@ + + + + + + + + + OK + + diff --git a/indexer/postgres/graphql-proxy/tsconfig.json b/indexer/postgres/graphql-proxy/tsconfig.json new file mode 100644 index 000000000000..77da9dd00982 --- /dev/null +++ b/indexer/postgres/graphql-proxy/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} \ No newline at end of file diff --git a/indexer/postgres/graphql.go b/indexer/postgres/graphql.go new file mode 100644 index 000000000000..c353c19f137d --- /dev/null +++ b/indexer/postgres/graphql.go @@ -0,0 +1,97 @@ +package postgres + +import ( + "database/sql" + "encoding/json" + "net/http" +) + +type graphqlHandler struct { + conn *sql.DB +} + +func NewGraphQLHandler(conn *sql.DB) http.Handler { + return &graphqlHandler{conn: conn} +} + +var _ http.Handler = &graphqlHandler{} + +type graphqlRequest struct { + Query string `json:"query"` + OperationName string `json:"operationName"` + Variables string `json:"variables"` +} + +type graphqlResponse struct { + Data json.RawMessage `json:"data"` + Errors []error `json:"errors,omitempty"` +} + +func (g graphqlHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + switch request.Method { + case http.MethodGet: + g.handleGet(writer, request) + case http.MethodPost: + g.handlePost(writer, request) + default: + http.Error(writer, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func (g graphqlHandler) handlePost(writer http.ResponseWriter, request *http.Request) { + var req graphqlRequest + err := json.NewDecoder(request.Body).Decode(&req) + if err != nil { + http.Error(writer, err.Error(), http.StatusBadRequest) + return + } + + g.handle(writer, request, &req) +} + +func (g graphqlHandler) handleGet(writer http.ResponseWriter, request *http.Request) { + var gqlReq graphqlRequest + gqlReq.Query = request.URL.Query().Get("query") + gqlReq.OperationName = request.URL.Query().Get("operationName") + gqlReq.Variables = request.URL.Query().Get("variables") + + g.handle(writer, request, &gqlReq) +} + +func (g graphqlHandler) handle(writer http.ResponseWriter, request *http.Request, gqlReq *graphqlRequest) { + rows, err := g.conn.QueryContext( + request.Context(), + `select graphql.resolve($1, $2, $3);`, + gqlReq.Query, + gqlReq.OperationName, + gqlReq.Variables, + ) + + if err != nil { + http.Error(writer, err.Error(), http.StatusInternalServerError) + return + } + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + + } + }(rows) + + var data json.RawMessage + for rows.Next() { + err = rows.Scan(&data) + if err != nil { + http.Error(writer, err.Error(), http.StatusInternalServerError) + return + } + } + + resp := graphqlResponse{Data: data} + err = json.NewEncoder(writer).Encode(resp) + if err != nil { + http.Error(writer, err.Error(), http.StatusInternalServerError) + } + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) +} diff --git a/indexer/postgres/indexer.go b/indexer/postgres/indexer.go new file mode 100644 index 000000000000..4d711e280257 --- /dev/null +++ b/indexer/postgres/indexer.go @@ -0,0 +1,164 @@ +package postgres + +import ( + "context" + "database/sql" + "encoding/json" + "fmt" + + "cosmossdk.io/schema/appdata" + "cosmossdk.io/schema/indexing" + "cosmossdk.io/schema/log" +) + +type Indexer struct { + ctx context.Context + db *sql.DB + tx *sql.Tx + options Options + + modules map[string]*ModuleManager +} + +func (i *Indexer) Initialize(ctx context.Context, data indexing.InitializationData) (indexing.InitializationResult, error) { + i.options.Logger.Info("Starting Postgres Indexer") + + go func() { + <-ctx.Done() + err := i.db.Close() + if err != nil { + panic(fmt.Sprintf("failed to close database: %v", err)) + } + }() + + i.ctx = ctx + + tx, err := i.db.BeginTx(ctx, nil) + if err != nil { + return indexing.InitializationResult{}, fmt.Errorf("failed to start transaction: %w", err) + } + + i.tx = tx + + return indexing.InitializationResult{ + Listener: i.listener(), + }, nil +} + +type configOptions struct { + DatabaseDriver string `json:"database_driver"` + DatabaseURL string `json:"database_url"` + RetainDeletions bool `json:"retain_deletions"` +} + +func init() { + indexing.RegisterIndexer("postgres", func(rawOpts map[string]interface{}, resources indexing.IndexerResources) (indexing.Indexer, error) { + bz, err := json.Marshal(rawOpts) + if err != nil { + return nil, fmt.Errorf("failed to marshal options: %w", err) + } + + var opts configOptions + err = json.Unmarshal(bz, &opts) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal options: %w", err) + } + + if opts.DatabaseDriver == "" { + opts.DatabaseDriver = "pgx" + } + + if opts.DatabaseURL == "" { + return nil, fmt.Errorf("connection URL not set") + } + + db, err := sql.Open(opts.DatabaseDriver, opts.DatabaseURL) + if err != nil { + return nil, fmt.Errorf("failed to open database: %w", err) + } + + return NewIndexer(db, Options{ + RetainDeletions: opts.RetainDeletions, + Logger: resources.Logger, + }) + }) +} + +type Options struct { + RetainDeletions bool + Logger log.Logger +} + +func NewIndexer(db *sql.DB, opts Options) (*Indexer, error) { + return &Indexer{ + db: db, + modules: map[string]*ModuleManager{}, + options: opts, + }, nil +} + +func (i *Indexer) listener() appdata.Listener { + return appdata.Listener{ + InitializeModuleData: i.initModuleSchema, + OnObjectUpdate: i.onObjectUpdate, + Commit: i.commit, + } +} + +func (i *Indexer) initModuleSchema(data appdata.ModuleInitializationData) error { + moduleName := data.ModuleName + modSchema := data.Schema + _, ok := i.modules[moduleName] + if ok { + return fmt.Errorf("module %s already initialized", moduleName) + } + + mm := newModuleManager(moduleName, modSchema, i.options) + i.modules[moduleName] = mm + + return mm.Init(i.ctx, i.tx) +} + +func (i *Indexer) onObjectUpdate(data appdata.ObjectUpdateData) error { + module := data.ModuleName + mod, ok := i.modules[module] + if !ok { + return fmt.Errorf("module %s not initialized", module) + } + + for _, update := range data.Updates { + tm, ok := mod.Tables[update.TypeName] + if !ok { + return fmt.Errorf("object type %s not found in schema for module %s", update.TypeName, module) + } + + var err error + if update.Delete { + err = tm.Delete(i.ctx, i.tx, update.Key) + } else { + err = tm.InsertUpdate(i.ctx, i.tx, update.Key, update.Value) + } + if err != nil { + return err + } + } + return nil +} + +func (i *Indexer) commit(_ appdata.CommitData) error { + err := i.tx.Commit() + if err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + + i.tx, err = i.db.BeginTx(i.ctx, nil) + return err +} + +func (i *Indexer) ActiveTx() *sql.Tx { + return i.tx +} + +func (i *Indexer) Modules() map[string]*ModuleManager { + return i.modules +} diff --git a/indexer/postgres/insert_update.go b/indexer/postgres/insert_update.go new file mode 100644 index 000000000000..af651da7ad5a --- /dev/null +++ b/indexer/postgres/insert_update.go @@ -0,0 +1,101 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" + "io" + "strings" +) + +func (tm *TableManager) InsertUpdate(ctx context.Context, tx *sql.Tx, key, value interface{}) error { + exists, err := tm.Exists(ctx, tx, key) + if err != nil { + return err + } + + buf := new(strings.Builder) + var params []interface{} + if exists { + params, err = tm.UpdateSql(buf, key, value) + } else { + params, err = tm.InsertSql(buf, key, value) + } + + sqlStr := buf.String() + tm.options.Logger.Debug("Insert or Update", "sql", sqlStr, "params", params) + _, err = tx.ExecContext(ctx, sqlStr, params...) + return err +} + +func (tm *TableManager) InsertSql(w io.Writer, key, value interface{}) ([]interface{}, error) { + keyParams, keyCols, err := tm.bindKeyParams(key) + if err != nil { + return nil, err + } + + valueParams, valueCols, err := tm.bindValueParams(value) + if err != nil { + return nil, err + } + + var allParams []interface{} + allParams = append(allParams, keyParams...) + allParams = append(allParams, valueParams...) + + allCols := make([]string, 0, len(keyCols)+len(valueCols)) + allCols = append(allCols, keyCols...) + allCols = append(allCols, valueCols...) + + var paramBindings []string + for i := 1; i <= len(allCols); i++ { + paramBindings = append(paramBindings, fmt.Sprintf("$%d", i)) + } + + _, err = fmt.Fprintf(w, "INSERT INTO %q (%s) VALUES (%s);", tm.TableName(), + strings.Join(allCols, ", "), + strings.Join(paramBindings, ", "), + ) + return allParams, err +} + +func (tm *TableManager) UpdateSql(w io.Writer, key, value interface{}) ([]interface{}, error) { + _, err := fmt.Fprintf(w, "UPDATE %q SET ", tm.TableName()) + + valueParams, valueCols, err := tm.bindValueParams(value) + if err != nil { + return nil, err + } + + paramIdx := 1 + for i, col := range valueCols { + if i > 0 { + _, err = fmt.Fprintf(w, ", ") + if err != nil { + return nil, err + } + } + _, err = fmt.Fprintf(w, "%s = $%d", col, paramIdx) + if err != nil { + return nil, err + } + + paramIdx++ + } + + if tm.options.RetainDeletions && tm.typ.RetainDeletions { + _, err = fmt.Fprintf(w, ", _deleted = FALSE") + if err != nil { + return nil, err + } + } + + _, keyParams, err := tm.WhereSqlAndParams(w, key, paramIdx) + if err != nil { + return nil, err + } + + allParams := append(valueParams, keyParams...) + _, err = fmt.Fprintf(w, ";") + return allParams, err +} diff --git a/indexer/postgres/module_mgr.go b/indexer/postgres/module_mgr.go new file mode 100644 index 000000000000..8d6c7f403db8 --- /dev/null +++ b/indexer/postgres/module_mgr.go @@ -0,0 +1,57 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" + + "cosmossdk.io/schema" +) + +type ModuleManager struct { + moduleName string + schema schema.ModuleSchema + // TODO: make private or internal + Tables map[string]*TableManager + definedEnums map[string]schema.EnumDefinition + options Options +} + +func newModuleManager(moduleName string, modSchema schema.ModuleSchema, options Options) *ModuleManager { + return &ModuleManager{ + moduleName: moduleName, + schema: modSchema, + Tables: map[string]*TableManager{}, + definedEnums: map[string]schema.EnumDefinition{}, + options: options, + } +} + +func (m *ModuleManager) Init(ctx context.Context, tx *sql.Tx) error { + // create enum types + for _, typ := range m.schema.ObjectTypes { + err := m.createEnumTypesForFields(ctx, tx, typ.KeyFields) + if err != nil { + return err + } + + err = m.createEnumTypesForFields(ctx, tx, typ.ValueFields) + if err != nil { + return err + } + } + + // create tables for all object types + // NOTE: if we want to support foreign keys, we need to sort tables ind dependency order + for _, typ := range m.schema.ObjectTypes { + tm := NewTableManager(m.moduleName, typ, m.options) + m.Tables[typ.Name] = tm + err := tm.CreateTable(ctx, tx) + if err != nil { + return fmt.Errorf("failed to create table for %s in module %s: %w", typ.Name, m.moduleName, err) + } + } + + return nil + +} diff --git a/indexer/postgres/params.go b/indexer/postgres/params.go new file mode 100644 index 000000000000..1d9d04072161 --- /dev/null +++ b/indexer/postgres/params.go @@ -0,0 +1,117 @@ +package postgres + +import ( + "fmt" + "time" + + "github.com/cosmos/btcutil/bech32" + + "cosmossdk.io/schema" +) + +func (tm *TableManager) bindKeyParams(key interface{}) ([]interface{}, []string, error) { + n := len(tm.typ.KeyFields) + if n == 0 { + // singleton, set _id = 1 + return []interface{}{1}, []string{"_id"}, nil + } else if n == 1 { + return tm.bindParams(tm.typ.KeyFields, []interface{}{key}) + } else { + key, ok := key.([]interface{}) + if !ok { + return nil, nil, fmt.Errorf("expected key to be a slice") + } + + return tm.bindParams(tm.typ.KeyFields, key) + } +} + +func (tm *TableManager) bindValueParams(value interface{}) (params []interface{}, valueCols []string, err error) { + n := len(tm.typ.ValueFields) + if n == 0 { + return nil, nil, nil + } else if valueUpdates, ok := value.(schema.ValueUpdates); ok { + var e error + var fields []schema.Field + var params []interface{} + if err := valueUpdates.Iterate(func(name string, value interface{}) bool { + field, ok := tm.valueFields[name] + if !ok { + e = fmt.Errorf("unknown column %q", name) + return false + } + fields = append(fields, field) + params = append(params, value) + return true + }); err != nil { + return nil, nil, err + } + if e != nil { + return nil, nil, e + } + + return tm.bindParams(fields, params) + } else if n == 1 { + return tm.bindParams(tm.typ.ValueFields, []interface{}{value}) + } else { + values, ok := value.([]interface{}) + if !ok { + return nil, nil, fmt.Errorf("expected values to be a slice") + } + + return tm.bindParams(tm.typ.ValueFields, values) + } +} + +func (tm *TableManager) bindParams(fields []schema.Field, values []interface{}) ([]interface{}, []string, error) { + names := make([]string, 0, len(fields)) + params := make([]interface{}, 0, len(fields)) + for i, field := range fields { + if i >= len(values) { + return nil, nil, fmt.Errorf("missing value for field %q", field.Name) + } + + param, err := tm.bindParam(field, values[i]) + if err != nil { + return nil, nil, err + } + + name, err := tm.updatableColumnName(field) + if err != nil { + return nil, nil, err + } + + names = append(names, name) + params = append(params, param) + } + return params, names, nil +} + +func (tm *TableManager) bindParam(field schema.Field, value interface{}) (param interface{}, err error) { + param = value + if value == nil { + if !field.Nullable { + return nil, fmt.Errorf("expected non-null value for field %q", field.Name) + } + } else if field.Kind == schema.TimeKind { + t, ok := value.(time.Time) + if !ok { + return nil, fmt.Errorf("expected time.Time value for field %q, got %T", field.Name, value) + } + + param = t.UnixNano() + } else if field.Kind == schema.DurationKind { + t, ok := value.(time.Duration) + if !ok { + return nil, fmt.Errorf("expected time.Duration value for field %q, got %T", field.Name, value) + } + + param = int64(t) + } else if field.Kind == schema.Bech32AddressKind { + param, err = bech32.EncodeFromBase256(field.AddressPrefix, value.([]byte)) + if err != nil { + return nil, fmt.Errorf("encoding bech32 failed: %w", err) + } + } + return +} diff --git a/indexer/postgres/select.go b/indexer/postgres/select.go new file mode 100644 index 000000000000..5325353f9942 --- /dev/null +++ b/indexer/postgres/select.go @@ -0,0 +1,91 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" + "io" + "strings" +) + +func (tm *TableManager) Exists(ctx context.Context, tx *sql.Tx, key interface{}) (bool, error) { + buf := new(strings.Builder) + params, err := tm.ExistsSqlAndParams(buf, key) + if err != nil { + return false, err + } + + return tm.checkExists(ctx, tx, buf.String(), params) +} + +func (tm *TableManager) ExistsSqlAndParams(w io.Writer, key interface{}) ([]interface{}, error) { + _, err := fmt.Fprintf(w, "SELECT 1 FROM %q", tm.TableName()) + if err != nil { + return nil, err + } + + _, keyParams, err := tm.WhereSqlAndParams(w, key, 1) + if err != nil { + return nil, err + } + + _, err = fmt.Fprintf(w, ";") + return keyParams, err +} + +func (tm *TableManager) Equals(ctx context.Context, tx *sql.Tx, key, val interface{}) (bool, error) { + buf := new(strings.Builder) + params, err := tm.EqualsSqlAndParams(buf, key, val) + if err != nil { + return false, err + } + + return tm.checkExists(ctx, tx, buf.String(), params) +} + +func (tm *TableManager) EqualsSqlAndParams(w io.Writer, key, val interface{}) ([]interface{}, error) { + _, err := fmt.Fprintf(w, "SELECT 1 FROM %q", tm.TableName()) + if err != nil { + return nil, err + } + + keyParams, keyCols, err := tm.bindKeyParams(key) + if err != nil { + return nil, err + } + + valueParams, valueCols, err := tm.bindValueParams(val) + if err != nil { + return nil, err + } + + allParams := make([]interface{}, 0, len(keyParams)+len(valueParams)) + allParams = append(allParams, keyParams...) + allParams = append(allParams, valueParams...) + + allCols := make([]string, 0, len(keyCols)+len(valueCols)) + allCols = append(allCols, keyCols...) + allCols = append(allCols, valueCols...) + + _, allParams, err = tm.WhereSql(w, allParams, allCols, 1) + if err != nil { + return nil, err + } + + _, err = fmt.Fprintf(w, ";") + return allParams, err +} + +func (tm *TableManager) checkExists(ctx context.Context, tx *sql.Tx, sqlStr string, params []interface{}) (bool, error) { + tm.options.Logger.Debug("Select", "sql", sqlStr, "params", params) + var res interface{} + err := tx.QueryRowContext(ctx, sqlStr, params...).Scan(&res) + switch err { + case nil: + return true, nil + case sql.ErrNoRows: + return false, nil + default: + return false, err + } +} diff --git a/indexer/postgres/table_mgr.go b/indexer/postgres/table_mgr.go new file mode 100644 index 000000000000..9210230bcc41 --- /dev/null +++ b/indexer/postgres/table_mgr.go @@ -0,0 +1,43 @@ +package postgres + +import ( + "fmt" + + "cosmossdk.io/schema" +) + +// TableManager is a helper struct that generates SQL for a given object type. +type TableManager struct { + moduleName string + typ schema.ObjectType + valueFields map[string]schema.Field + allFields map[string]schema.Field + options Options +} + +// NewTableManager creates a new TableManager for the given object type. +func NewTableManager(moduleName string, typ schema.ObjectType, options Options) *TableManager { + allFields := make(map[string]schema.Field) + valueFields := make(map[string]schema.Field) + + for _, field := range typ.KeyFields { + allFields[field.Name] = field + } + + for _, field := range typ.ValueFields { + valueFields[field.Name] = field + allFields[field.Name] = field + } + + return &TableManager{ + moduleName: moduleName, + typ: typ, + allFields: allFields, + valueFields: valueFields, + options: options, + } +} + +func (tm *TableManager) TableName() string { + return fmt.Sprintf("%s_%s", tm.moduleName, tm.typ.Name) +} diff --git a/indexer/postgres/testing/app/main.go b/indexer/postgres/testing/app/main.go new file mode 100644 index 000000000000..9569c0d53187 --- /dev/null +++ b/indexer/postgres/testing/app/main.go @@ -0,0 +1,100 @@ +package main + +import ( + "context" + "database/sql" + "os" + + "cosmossdk.io/log" + embeddedpostgres "github.com/fergusstrange/embedded-postgres" + "github.com/hashicorp/consul/sdk/freeport" + _ "github.com/jackc/pgx/v5/stdlib" + + "cosmossdk.io/indexer/postgres" + "cosmossdk.io/schema/indexing" + indexertesting "cosmossdk.io/schema/testing" + appdatatest "cosmossdk.io/schema/testing/appdata" + "cosmossdk.io/schema/testing/statesim" +) + +func start() error { + dbUrl, found := os.LookupEnv("DATABASE_URL") + if !found { + tempDir, err := os.MkdirTemp("", "postgres-indexer-test") + if err != nil { + return err + } + defer func(path string) { + err := os.RemoveAll(path) + if err != nil { + panic(err) + } + }(tempDir) + + dbPort := freeport.MustTake(1)[0] + pgConfig := embeddedpostgres.DefaultConfig(). + Port(uint32(dbPort)). + DataPath(tempDir) + + dbUrl = pgConfig.GetConnectionURL() + pg := embeddedpostgres.NewDatabase(pgConfig) + err = pg.Start() + if err != nil { + return err + } + defer func(pg *embeddedpostgres.EmbeddedPostgres) { + err := pg.Stop() + if err != nil { + panic(err) + } + }(pg) + } + + db, err := sql.Open("pgx", dbUrl) + if err != nil { + return err + } + + indexer, err := postgres.NewIndexer(db, postgres.Options{ + Logger: log.NewLogger(os.Stdout), + }) + if err != nil { + return err + } + + res, err := indexer.Initialize(context.Background(), indexing.InitializationData{}) + if err != nil { + return err + } + + fixture := appdatatest.NewSimulator(appdatatest.SimulatorOptions{ + Listener: res.Listener, + AppSchema: indexertesting.ExampleAppSchema, + StateSimOptions: statesim.Options{}, + }) + + err = fixture.Initialize() + if err != nil { + return err + } + + blockDataGen := fixture.BlockDataGenN(1000) + i := 0 + for { + blockData := blockDataGen.Example(i) + err = fixture.ProcessBlockData(blockData) + if err != nil { + return err + } + i++ + } + + return nil +} + +func main() { + err := start() + if err != nil { + panic(err) + } +} diff --git a/indexer/postgres/testing/go.mod b/indexer/postgres/testing/go.mod new file mode 100644 index 000000000000..76dbbab8c311 --- /dev/null +++ b/indexer/postgres/testing/go.mod @@ -0,0 +1,44 @@ +module cosmossdk.io/indexer/postgres/testing + +require ( + cosmossdk.io/indexer/postgres v0.0.0 + cosmossdk.io/log v1.3.1 + cosmossdk.io/schema v0.0.0 + cosmossdk.io/schema/testing v0.0.0-00010101000000-000000000000 + github.com/fergusstrange/embedded-postgres v1.27.0 + github.com/hashicorp/consul/sdk v0.16.1 + github.com/jackc/pgx/v5 v5.6.0 + github.com/stretchr/testify v1.9.0 +) + +require ( + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.4 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/zerolog v1.32.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + pgregory.net/rapid v1.1.0 // indirect +) + +replace cosmossdk.io/schema/testing => ../../../schema/testing + +replace cosmossdk.io/indexer/postgres => .. + +replace cosmossdk.io/schema => ../../../schema + +go 1.22 diff --git a/indexer/postgres/testing/go.sum b/indexer/postgres/testing/go.sum new file mode 100644 index 000000000000..6f4fc275583d --- /dev/null +++ b/indexer/postgres/testing/go.sum @@ -0,0 +1,78 @@ +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fergusstrange/embedded-postgres v1.27.0 h1:RAlpWL194IhEpPgeJceTM0ifMJKhiSVxBVIDYB1Jee8= +github.com/fergusstrange/embedded-postgres v1.27.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= +github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/indexer/postgres/testing/postgres_test.go b/indexer/postgres/testing/postgres_test.go new file mode 100644 index 000000000000..d7af95cc1d8c --- /dev/null +++ b/indexer/postgres/testing/postgres_test.go @@ -0,0 +1,113 @@ +package testing + +import ( + "context" + "database/sql" + "fmt" + "os" + "testing" + + "cosmossdk.io/log" + embeddedpostgres "github.com/fergusstrange/embedded-postgres" + "github.com/hashicorp/consul/sdk/freeport" + _ "github.com/jackc/pgx/v5/stdlib" + "github.com/stretchr/testify/require" + + "cosmossdk.io/indexer/postgres" + "cosmossdk.io/schema" + "cosmossdk.io/schema/appdata" + "cosmossdk.io/schema/indexing" + indexertesting "cosmossdk.io/schema/testing" + appdatatest "cosmossdk.io/schema/testing/appdata" + "cosmossdk.io/schema/testing/statesim" +) + +func TestPostgresIndexer(t *testing.T) { + t.Run("RetainDeletions", func(t *testing.T) { + testPostgresIndexer(t, true) + }) + t.Run("NoRetainDeletions", func(t *testing.T) { + testPostgresIndexer(t, false) + }) +} + +func testPostgresIndexer(t *testing.T, retainDeletions bool) { + tempDir, err := os.MkdirTemp("", "postgres-indexer-test") + require.NoError(t, err) + + dbPort := freeport.GetOne(t) + pgConfig := embeddedpostgres.DefaultConfig(). + Port(uint32(dbPort)). + DataPath(tempDir) + + dbUrl := pgConfig.GetConnectionURL() + pg := embeddedpostgres.NewDatabase(pgConfig) + require.NoError(t, pg.Start()) + + ctx, cancel := context.WithCancel(context.Background()) + + t.Cleanup(func() { + cancel() + require.NoError(t, pg.Stop()) + err := os.RemoveAll(tempDir) + require.NoError(t, err) + }) + + db, err := sql.Open("pgx", dbUrl) + require.NoError(t, err) + + indexer, err := postgres.NewIndexer(db, postgres.Options{ + RetainDeletions: retainDeletions, + Logger: log.NewTestLogger(t), + }) + require.NoError(t, err) + + res, err := indexer.Initialize(ctx, indexing.InitializationData{}) + require.NoError(t, err) + + fixture := appdatatest.NewSimulator(appdatatest.SimulatorOptions{ + Listener: appdata.ListenerMux( + appdata.DebugListener(os.Stdout), + res.Listener, + ), + AppSchema: indexertesting.ExampleAppSchema, + StateSimOptions: statesim.Options{ + CanRetainDeletions: retainDeletions, + }, + }) + + require.NoError(t, fixture.Initialize()) + + blockDataGen := fixture.BlockDataGenN(1000) + for i := 0; i < 1000; i++ { + blockData := blockDataGen.Example(i) + require.NoError(t, fixture.ProcessBlockData(blockData)) + + require.NoError(t, fixture.AppState().ScanObjectCollections(func(moduleName string, collection *statesim.ObjectCollection) error { + modMgr, ok := indexer.Modules()[moduleName] + require.True(t, ok) + tblMgr, ok := modMgr.Tables[collection.ObjectType().Name] + require.True(t, ok) + + expectedCount := collection.Len() + actualCount, err := tblMgr.Count(context.Background(), indexer.ActiveTx()) + require.NoError(t, err) + require.Equalf(t, expectedCount, actualCount, "table %s %s count mismatch", moduleName, collection.ObjectType().Name) + + return collection.ScanState(func(update schema.ObjectUpdate) error { + found, err := tblMgr.Equals( + context.Background(), + indexer.ActiveTx(), update.Key, update.Value) + if err != nil { + return err + } + + if !found { + return fmt.Errorf("object not found in table %s %s %v %v", moduleName, collection.ObjectType().Name, update.Key, update.Value) + } + + return nil + }) + })) + } +} diff --git a/indexer/postgres/where.go b/indexer/postgres/where.go new file mode 100644 index 000000000000..81e63fc20de6 --- /dev/null +++ b/indexer/postgres/where.go @@ -0,0 +1,58 @@ +package postgres + +import ( + "fmt" + "io" +) + +func (tm *TableManager) WhereSqlAndParams(w io.Writer, key interface{}, startParamIdx int) (endParamIdx int, keyParams []interface{}, err error) { + var keyCols []string + keyParams, keyCols, err = tm.bindKeyParams(key) + if err != nil { + return + } + + endParamIdx, keyParams, err = tm.WhereSql(w, keyParams, keyCols, startParamIdx) + return +} + +func (tm *TableManager) WhereSql(w io.Writer, params []interface{}, cols []string, startParamIdx int) (endParamIdx int, resParams []interface{}, err error) { + _, err = fmt.Fprintf(w, " WHERE ") + if err != nil { + return + } + + endParamIdx = startParamIdx + for i, col := range cols { + if i > 0 { + _, err = fmt.Fprintf(w, " AND ") + if err != nil { + return + } + } + + _, err = fmt.Fprintf(w, "%s ", col) + if err != nil { + return + } + + if params[i] == nil { + _, err = fmt.Fprintf(w, "IS NULL") + if err != nil { + return + } + + } else { + _, err = fmt.Fprintf(w, "= $%d", endParamIdx) + if err != nil { + return + } + + resParams = append(resParams, params[i]) + + endParamIdx++ + } + } + + return +} diff --git a/indexer/source/sdk0/v47/go.mod b/indexer/source/sdk0/v47/go.mod new file mode 100644 index 000000000000..4b4a946923ff --- /dev/null +++ b/indexer/source/sdk0/v47/go.mod @@ -0,0 +1,3 @@ +module cosmossdk.io/indexer/source/sdk0/v47 + +go 1.22 diff --git a/indexer/source/sdk0/v47/source.go b/indexer/source/sdk0/v47/source.go new file mode 100644 index 000000000000..113139ef444b --- /dev/null +++ b/indexer/source/sdk0/v47/source.go @@ -0,0 +1,4 @@ +package v47 + +type Source struct { +} diff --git a/indexer/source/store/go.mod b/indexer/source/store/go.mod new file mode 100644 index 000000000000..d9210667644d --- /dev/null +++ b/indexer/source/store/go.mod @@ -0,0 +1,66 @@ +module store + +go 1.22.2 + +toolchain go1.22.3 + +require ( + cosmossdk.io/core v0.12.0 + cosmossdk.io/indexer/base v0.0.0-00010101000000-000000000000 + cosmossdk.io/store v1.1.0 + //github.com/cometbft/cometbft v0.38.7 + github.com/cometbft/cometbft v1.0.0-alpha.2.0.20240530055211-ae27f7eb3c08 +) + +require ( + cosmossdk.io/errors v1.0.1 // indirect + cosmossdk.io/math v1.3.0 // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft/api v1.0.0-rc.1 // indirect + github.com/cosmos/cosmos-db v1.0.2 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/gogoproto v1.5.0 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.3 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/klauspost/compress v1.17.8 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/linxGnu/grocksdb v1.8.14 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.54.0 // indirect + github.com/prometheus/procfs v0.14.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect +) + +replace cosmossdk.io/indexer/base => ../../base + +replace cosmossdk.io/store => ../../../store + +replace cosmossdk.io/core => ../../../core diff --git a/indexer/source/store/go.sum b/indexer/source/store/go.sum new file mode 100644 index 000000000000..d8a6b11ff5c0 --- /dev/null +++ b/indexer/source/store/go.sum @@ -0,0 +1,329 @@ +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= +cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/cometbft/cometbft v1.0.0-alpha.2.0.20240530055211-ae27f7eb3c08 h1:xW0les/5Lr+4qVsOYDDwl5Utj8uouvv69hEtiavHJ2M= +github.com/cometbft/cometbft v1.0.0-alpha.2.0.20240530055211-ae27f7eb3c08/go.mod h1:8uXGKqp5yPmNSbU81K/669E3rfO3H+juGkYKCPTnAtY= +github.com/cometbft/cometbft/api v1.0.0-alpha.2.0.20240429102542-490e9bc3de65 h1:Bj+DkG59qYZE54uWBKXsNtiug1u6M4x8Tk3l5tAb/3I= +github.com/cometbft/cometbft/api v1.0.0-alpha.2.0.20240429102542-490e9bc3de65/go.mod h1:NDFKiBBD8HJC6QQLAoUI99YhsiRZtg2+FJWfk6A6m6o= +github.com/cometbft/cometbft/api v1.0.0-rc.1/go.mod h1:NDFKiBBD8HJC6QQLAoUI99YhsiRZtg2+FJWfk6A6m6o= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= +github.com/cosmos/gogoproto v1.4.12 h1:vB6Lbe/rtnYGjQuFxkPiPYiCybqFT8QvLipDZP8JpFE= +github.com/cosmos/gogoproto v1.4.12/go.mod h1:LnZob1bXRdUoqMMtwYlcR3wjiElmlC+FkjaZRv1/eLY= +github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= +github.com/cosmos/iavl v1.1.4 h1:Z0cVVjeQqOUp78/nWt/uhQy83vYluWlAMGQ4zbH9G34= +github.com/cosmos/iavl v1.1.4/go.mod h1:vCYmRQUJU1wwj0oRD3wMEtOM9sJNDP+GFMaXmIxZ/rU= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= +github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= +github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/petermattis/goid v0.0.0-20221215004737-a150e88a970d h1:htwtWgtQo8YS6JFWWi2DNgY0RwSGJ1ruMoxY6CUUclk= +github.com/petermattis/goid v0.0.0-20221215004737-a150e88a970d/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= +github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.14.0 h1:Lw4VdGGoKEZilJsayHf0B+9YgLGREba2C6xr+Fdfq6s= +github.com/prometheus/procfs v0.14.0/go.mod h1:XL+Iwz8k8ZabyZfMFHPiilCniixqQarAy5Mu67pHlNQ= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/indexer/source/store/source.go b/indexer/source/store/source.go new file mode 100644 index 000000000000..98337d275d62 --- /dev/null +++ b/indexer/source/store/source.go @@ -0,0 +1,51 @@ +package storesource + +import ( + "context" + + abci "github.com/cometbft/cometbft/abci/types" + + "cosmossdk.io/core/appmodule" + storetypes "cosmossdk.io/store/types" + + "cosmossdk.io/indexer/base" +) + +type source struct { + engine *indexerbase.Engine +} + +type Options struct { + indexerbase.EngineOptions[appmodule.AppModule] +} + +func NewSource(opts Options) storetypes.ABCIListener { + engine := indexerbase.NewEngine(opts.EngineOptions) + return &source{engine: engine} +} + +func (s source) ListenFinalizeBlock(ctx context.Context, req abci.FinalizeBlockRequest, res abci.FinalizeBlockResponse) error { + //TODO implement me + // block + // txs + // events - missing event msg index + return nil +} + +func (s source) ListenCommit(ctx context.Context, res abci.CommitResponse, changeSet []*storetypes.StoreKVPair) error { + for _, kv := range changeSet { + if kv.Delete { + if err := s.engine.ReceiveStateDelete(kv.StoreKey, kv.Key, false); err != nil { + return err + } else { + if err := s.engine.ReceiveStateSet(kv.StoreKey, kv.Key, kv.Value); err != nil { + return err + } + } + } + return nil + } + return s.engine.Commit() +} + +var _ storetypes.ABCIListener = &source{} diff --git a/schema/README.md b/schema/README.md index d19d54c9e8c4..316a57e67386 100644 --- a/schema/README.md +++ b/schema/README.md @@ -2,7 +2,7 @@ The `cosmossdk.io/schema` base module is designed to provide a stable, **zero-dependency** base layer for specifying the **logical representation of module state schemas** and implementing **state indexing**. This is intended to be used primarily for indexing modules in external databases and providing a standard human-readable state representation for genesis import and export. -The schema defined in this library does not aim to be general purpose and cover all types of schemas, such as those used for defining transactions. For instance, this schema does not include many types of composite objects such as nested objects and arrays. Rather, the schema defined here aims to cover _state_ schemas only which are implemented as key-value pairs and usually have direct mappings to relational database tables or objects in a document store. +The schema defined in this library does not aim to be general purpose and cover all types of schemas, such as those used for defining transactions. For instance, this schema is not include many types of composite objects as nested objects and arrays. Rather, the schema defined here aims to cover _state_ schemas only which are implemented as key-value pairs and usually have direct mappings to relational database tables or objects in a document store. Also, this schema does not cover physical state layout and byte-level encoding, but simply describes a common logical format. diff --git a/schema/appdata/README.md b/schema/appdata/README.md index f6a3e6766373..d750e42efa56 100644 --- a/schema/appdata/README.md +++ b/schema/appdata/README.md @@ -1,6 +1,6 @@ -# App Data +# Listener -The `appdata` package defines the basic types for streaming blockchain event and state data to external listeners, with a specific focus on supporting logical decoding and indexing of state. +The `listener` package defines the basic types for streaming blockchain event and state data to external listeners, with a specific focus on supporting logical decoding and indexing of state. A blockchain data source should accept a `Listener` instance and invoke the provided callbacks in the correct order. A downstream listener should provide a `Listener` instance and perform operations based on the data passed to its callbacks. @@ -29,4 +29,4 @@ sequenceDiagram Sources will generally only call `InitializeModuleSchema` and `OnObjectUpdate` if they have native logical decoding capabilities. Usually, the indexer framework will provide this functionality based on `OnKVPair` data and `schema.HasModuleCodec` implementations. -`StartBlock` and `OnBlockHeader` should be called only once at the beginning of a block, and `Commit` should be called only once at the end of a block. The `OnTx`, `OnEvent`, `OnKVPair` and `OnObjectUpdate` must be called after `OnBlockHeader`, may be called multiple times within a block and indexers should not assume that the order is logical unless `InitializationData.HasEventAlignedWrites` is true. +`StartBlock` and `OnBlockHeader` should be called only once at the beginning of a block, and `Commit` should be called only once at the end of a block. The `OnTx`, `OnEvent`, `OnKVPair` and `OnObjectUpdate` must be called after `OnBlockHeader`, may be called multiple times within a block and indexers should not assume that the order is logical unless `InitializationData.HasEventAlignedWrites` is true. diff --git a/schema/appdata/async.go b/schema/appdata/async.go new file mode 100644 index 000000000000..47ffc707fe54 --- /dev/null +++ b/schema/appdata/async.go @@ -0,0 +1,115 @@ +package appdata + +func AsyncListener(listener Listener, bufferSize int, commitChan chan<- error, doneChan <-chan struct{}) Listener { + packetChan := make(chan Packet, bufferSize) + res := Listener{} + + go func() { + var err error + for { + select { + case packet := <-packetChan: + if err != nil { + // if we have an error, don't process any more packets + // and return the error and finish when it's time to commit + if _, ok := packet.(CommitData); ok { + commitChan <- err + return + } + } else { + // process the packet + err = listener.SendPacket(packet) + // if it's a commit + if _, ok := packet.(CommitData); ok { + commitChan <- err + if err != nil { + return + } + } + } + + case <-doneChan: + return + } + } + }() + + if listener.InitializeModuleData != nil { + res.InitializeModuleData = func(data ModuleInitializationData) error { + packetChan <- data + return nil + } + } + + if listener.StartBlock != nil { + res.StartBlock = func(data StartBlockData) error { + packetChan <- data + return nil + } + } + + if listener.OnTx != nil { + res.OnTx = func(data TxData) error { + packetChan <- data + return nil + } + } + + if listener.OnEvent != nil { + res.OnEvent = func(data EventData) error { + packetChan <- data + return nil + } + } + + if listener.OnKVPair != nil { + res.OnKVPair = func(data KVPairData) error { + packetChan <- data + return nil + } + } + + if listener.OnObjectUpdate != nil { + res.OnObjectUpdate = func(data ObjectUpdateData) error { + packetChan <- data + return nil + } + } + + if listener.Commit != nil { + res.Commit = func(data CommitData) error { + packetChan <- data + return nil + } + } + + return res +} + +func AsyncListenerMux(listeners []Listener, bufferSize int, doneChan <-chan struct{}) Listener { + asyncListeners := make([]Listener, len(listeners)) + commitChans := make([]chan error, len(listeners)) + for i, l := range listeners { + commitChan := make(chan error) + commitChans[i] = commitChan + asyncListeners[i] = AsyncListener(l, bufferSize, commitChan, doneChan) + } + mux := ListenerMux(asyncListeners...) + muxCommit := mux.Commit + mux.Commit = func(data CommitData) error { + err := muxCommit(data) + if err != nil { + return err + } + + for _, commitChan := range commitChans { + err := <-commitChan + if err != nil { + return err + } + } + return nil + } + + return mux +} diff --git a/schema/appdata/debug.go b/schema/appdata/debug.go new file mode 100644 index 000000000000..bff6a851d533 --- /dev/null +++ b/schema/appdata/debug.go @@ -0,0 +1,31 @@ +package appdata + +import ( + "fmt" + "io" +) + +func DebugListener(out io.Writer) Listener { + res := packetForwarder(func(p Packet) error { + _, err := fmt.Fprintln(out, p) + return err + }) + //res.Initialize = func(ctx context.Context, data InitializationData) (lastBlockPersisted int64, err error) { + // _, err = fmt.Fprintf(out, "Initialize: %v\n", data) + // return 0, err + //} + return res +} + +func packetForwarder(f func(Packet) error) Listener { + return Listener{ + //Initialize: nil, // can't be forwarded + InitializeModuleData: func(data ModuleInitializationData) error { return f(data) }, + OnTx: func(data TxData) error { return f(data) }, + OnEvent: func(data EventData) error { return f(data) }, + OnKVPair: func(data KVPairData) error { return f(data) }, + OnObjectUpdate: func(data ObjectUpdateData) error { return f(data) }, + StartBlock: func(data StartBlockData) error { return f(data) }, + Commit: func(data CommitData) error { return f(data) }, + } +} diff --git a/schema/appdata/mux.go b/schema/appdata/mux.go new file mode 100644 index 000000000000..af2ffd9e3145 --- /dev/null +++ b/schema/appdata/mux.go @@ -0,0 +1,121 @@ +package appdata + +// ListenerMux returns a listener that forwards received events to all the provided listeners and only +// registers a callback if a non-nil callback is present in at least one of the listeners. +func ListenerMux(listeners ...Listener) Listener { + mux := Listener{} + + for _, l := range listeners { + if l.InitializeModuleData != nil { + mux.InitializeModuleData = func(data ModuleInitializationData) error { + for _, l := range listeners { + if l.InitializeModuleData != nil { + if err := l.InitializeModuleData(data); err != nil { + return err + } + } + } + return nil + } + break + } + } + + for _, l := range listeners { + if l.StartBlock != nil { + mux.StartBlock = func(data StartBlockData) error { + for _, l := range listeners { + if l.StartBlock != nil { + if err := l.StartBlock(data); err != nil { + return err + } + } + } + return nil + } + break + } + } + + for _, l := range listeners { + if l.OnTx != nil { + mux.OnTx = func(data TxData) error { + for _, l := range listeners { + if l.OnTx != nil { + if err := l.OnTx(data); err != nil { + return err + } + } + } + return nil + } + break + } + } + + for _, listener := range listeners { + if listener.OnEvent != nil { + mux.OnEvent = func(data EventData) error { + for _, l := range listeners { + if l.OnEvent != nil { + if err := l.OnEvent(data); err != nil { + return err + } + } + } + return nil + } + break + } + } + + for _, listener := range listeners { + if listener.OnKVPair != nil { + mux.OnKVPair = func(data KVPairData) error { + for _, l := range listeners { + if l.OnKVPair != nil { + if err := l.OnKVPair(data); err != nil { + return err + } + } + } + return nil + } + break + } + } + + for _, listener := range listeners { + if listener.OnObjectUpdate != nil { + mux.OnObjectUpdate = func(data ObjectUpdateData) error { + for _, l := range listeners { + if l.OnObjectUpdate != nil { + if err := l.OnObjectUpdate(data); err != nil { + return err + } + } + } + return nil + } + break + } + } + + for _, listener := range listeners { + if listener.Commit != nil { + mux.Commit = func(data CommitData) error { + for _, l := range listeners { + if l.Commit != nil { + if err := l.Commit(data); err != nil { + return err + } + } + } + return nil + } + break + } + } + + return mux +} diff --git a/schema/decoding/README.md b/schema/decoding/README.md new file mode 100644 index 000000000000..21b5dd2fd2a5 --- /dev/null +++ b/schema/decoding/README.md @@ -0,0 +1,42 @@ +# Indexer Base + +The indexer base module is designed to provide a stable, zero-dependency base layer for the built-in indexer functionality. Packages that integrate with the indexer should feel free to depend on this package without fear of any external dependencies being pulled in. + +The basic types for specifying index sources, targets and decoders are provided here. An indexing source should accept a `Listener` instance and invoke the provided callbacks in the correct order. An indexer should provide a `Listener` instance and perform indexing operations based on the data passed to its callbacks. A module that exposes logical updates in the form of `ObjectUpdate`s should implement the `IndexableModule` interface. + +## `Listener` Callback Order + +`Listener` callbacks should be called in this order + +```mermaid +sequenceDiagram + actor Source + actor Manager + participant Indexer + Source -->> Manager: InitializeModuleSchema + Manager ->> Indexer: InitializeModuleSchema + Source ->> Manager: Initialize + Manager ->> Indexer: Initialize + loop Block + Source ->> Manager: StartBlock + Manager ->> Indexer: StartBlock + Source -->> Manager: OnBlockHeader + Manager -->> Indexer: OnBlockHeader + Source -->> Manager: OnTx + Manager -->> Indexer: OnTx + Source -->> Manager: OnEvent + Manager -->> Indexer: OnEvent + Source -->> Manager: OnKVPair + Manager -->> Indexer: OnKVPair + Source -->> Manager: OnObjectUpdate + Manager -->> Indexer: OnObjectUpdate + Source ->> Manager: Commit + Manager ->> Indexer: Commit + end +``` + +`InitializeModuleSchema` should be called at most once for every module with logical data and all calls to should happen even before `Initialize` is called. After that `Initialize` MUST be called before any other method and should only be invoked once. + +Sources will generally only call `InitializeModuleSchema` and `OnObjectUpdate` if they have native logical decoding capabilities. Usually, the indexer framework will provide this functionality based on `OnKVPair` data and `IndexableModule` implementations. + +`StartBlock` and `OnBlockHeader` should be called only once at the beginning of a block, and `Commit` should be called only once at the end of a block. The `OnTx`, `OnEvent`, `OnKVPair` and `OnObjectUpdate` must be called after `OnBlockHeader`, may be called multiple times within a block and indexers should not assume that the order is logical unless `InitializationData.HasEventAlignedWrites` is true. diff --git a/schema/decoding/middleware.go b/schema/decoding/middleware.go new file mode 100644 index 000000000000..3364a3bc9728 --- /dev/null +++ b/schema/decoding/middleware.go @@ -0,0 +1,87 @@ +package decoding + +import ( + "fmt" + + "cosmossdk.io/schema" + "cosmossdk.io/schema/appdata" + "cosmossdk.io/schema/log" +) + +type Options struct { + DecoderResolver DecoderResolver + + // SyncSource is the source that will be used do initial indexing of modules with pre-existing + // state. It is optional, but if it is not provided, indexing can only be starting when a node + // is synced from genesis. + SyncSource SyncSource + Logger log.Logger +} + +func Middleware(target appdata.Listener, opts Options) (appdata.Listener, error) { + initializeModuleData := target.InitializeModuleData + onKVPair := target.OnKVPair + + moduleCodecs := map[string]schema.ModuleCodec{} + if opts.DecoderResolver != nil { + err := opts.DecoderResolver.Iterate(func(moduleName string, codec schema.ModuleCodec) error { + opts.Logger.Info("Initializing module schema", "moduleName", moduleName) + + if _, ok := moduleCodecs[moduleName]; ok { + return fmt.Errorf("module %s already initialized", moduleName) + } + + if err := codec.Schema.Validate(); err != nil { + return fmt.Errorf("error validating schema for module %s: %w", moduleName, err) + } + + moduleCodecs[moduleName] = codec + if initializeModuleData != nil { + return initializeModuleData(appdata.ModuleInitializationData{ + ModuleName: moduleName, + Schema: codec.Schema, + }) + } + return nil + }) + if err != nil { + return appdata.Listener{}, err + } + } + + // TODO: catch-up sync + + target.OnKVPair = func(data appdata.KVPairData) error { + if onKVPair != nil { + return onKVPair(data) + } + + if target.OnObjectUpdate != nil { + for _, kvUpdate := range data.Updates { + codec, ok := moduleCodecs[kvUpdate.ModuleName] + if !ok { + // TODO handle discovering a new module + return nil + } + + updates, err := codec.KVDecoder(kvUpdate.Update) + if err != nil { + return err + } + + if !ok { + return nil + } + + return target.OnObjectUpdate(appdata.ObjectUpdateData{ + ModuleName: kvUpdate.ModuleName, + Updates: updates, + }) + } + } + + return nil + } + + return target, nil +} diff --git a/schema/decoding/resolver.go b/schema/decoding/resolver.go new file mode 100644 index 000000000000..ef2c720eb411 --- /dev/null +++ b/schema/decoding/resolver.go @@ -0,0 +1,71 @@ +package decoding + +import ( + "sort" + + "cosmossdk.io/schema" +) + +type DecoderResolver interface { + // Iterate iterates over all module decoders which should be initialized at startup. + Iterate(func(string, schema.ModuleCodec) error) error + + // LookupDecoder allows for resolving decoders dynamically. For instance, some module-like + // things may come into existence dynamically (like x/accounts or EVM or WASM contracts). + // The first time the manager sees one of these appearing in KV-store writes, it will + // lookup a decoder for it and cache it for future use. The manager will also perform + // a catch-up sync before passing any new writes to ensure that all historical state has + // been synced if there is any This check will only happen the first time a module is seen + // by the manager in a given process (a process restart will cause this check to happen again). + LookupDecoder(moduleName string) (decoder schema.ModuleCodec, found bool, err error) +} + +type moduleSetDecoderResolver struct { + moduleSet map[string]interface{} +} + +// ModuleSetDecoderResolver returns DecoderResolver that will discover modules implementing +// DecodeableModule in the provided module set. +func ModuleSetDecoderResolver(moduleSet map[string]interface{}) DecoderResolver { + return &moduleSetDecoderResolver{ + moduleSet: moduleSet, + } +} + +func (a moduleSetDecoderResolver) Iterate(f func(string, schema.ModuleCodec) error) error { + keys := make([]string, 0, len(a.moduleSet)) + for k := range a.moduleSet { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + module := a.moduleSet[k] + dm, ok := module.(schema.HasModuleCodec) + if ok { + decoder, err := dm.ModuleCodec() + if err != nil { + return err + } + err = f(k, decoder) + if err != nil { + return err + } + } + } + return nil +} + +func (a moduleSetDecoderResolver) LookupDecoder(moduleName string) (schema.ModuleCodec, bool, error) { + mod, ok := a.moduleSet[moduleName] + if !ok { + return schema.ModuleCodec{}, false, nil + } + + dm, ok := mod.(schema.HasModuleCodec) + if !ok { + return schema.ModuleCodec{}, false, nil + } + + decoder, err := dm.ModuleCodec() + return decoder, true, err +} diff --git a/schema/decoding/sync.go b/schema/decoding/sync.go new file mode 100644 index 000000000000..1bcd56fa0f89 --- /dev/null +++ b/schema/decoding/sync.go @@ -0,0 +1,8 @@ +package decoding + +// SyncSource is an interface that allows indexers to start indexing modules with pre-existing state. +type SyncSource interface { + + // IterateAllKVPairs iterates over all key-value pairs for a given module. + IterateAllKVPairs(moduleName string, fn func(key, value []byte) error) error +} diff --git a/schema/indexing/indexer.go b/schema/indexing/indexer.go new file mode 100644 index 000000000000..926392952153 --- /dev/null +++ b/schema/indexing/indexer.go @@ -0,0 +1,36 @@ +package indexing + +import ( + "context" + "fmt" + + "cosmossdk.io/schema/appdata" + "cosmossdk.io/schema/log" +) + +type Indexer interface { + Initialize(context.Context, InitializationData) (InitializationResult, error) +} + +type IndexerResources struct { + Logger log.Logger +} + +type IndexerFactory = func(options map[string]interface{}, resources IndexerResources) (Indexer, error) + +type InitializationData struct{} + +type InitializationResult struct { + Listener appdata.Listener + LastBlockPersisted int64 +} + +func RegisterIndexer(name string, factory IndexerFactory) { + if _, ok := indexerRegistry[name]; ok { + panic(fmt.Sprintf("indexer %s already registered", name)) + } + + indexerRegistry[name] = factory +} + +var indexerRegistry = map[string]IndexerFactory{} diff --git a/schema/indexing/logger.go b/schema/indexing/logger.go new file mode 100644 index 000000000000..900a8d89cebe --- /dev/null +++ b/schema/indexing/logger.go @@ -0,0 +1 @@ +package indexing diff --git a/schema/indexing/manager.go b/schema/indexing/manager.go new file mode 100644 index 000000000000..c858fbdc4ee8 --- /dev/null +++ b/schema/indexing/manager.go @@ -0,0 +1,70 @@ +package indexing + +import ( + "context" + "fmt" + + "cosmossdk.io/schema/appdata" + "cosmossdk.io/schema/decoding" + "cosmossdk.io/schema/log" +) + +type Options struct { + Context context.Context + Options map[string]interface{} + Resolver decoding.DecoderResolver + SyncSource decoding.SyncSource + Logger log.Logger +} + +func Start(opts Options) (appdata.Listener, error) { + if opts.Logger == nil { + opts.Logger = log.NoopLogger{} + } + + opts.Logger.Info("Starting Indexer Manager") + + resources := IndexerResources{Logger: opts.Logger} + + var indexers []appdata.Listener + ctx := opts.Context + if ctx == nil { + ctx = context.Background() + } + + for indexerName, factory := range indexerRegistry { + indexerOpts, ok := opts.Options[indexerName] + if !ok { + continue + } + + if opts.Logger != nil { + opts.Logger.Info(fmt.Sprintf("Starting Indexer %s", indexerName), "options", indexerOpts) + } + + optsMap, ok := indexerOpts.(map[string]interface{}) + if !ok { + return appdata.Listener{}, fmt.Errorf("invalid indexer options type %T for %s, expected a map", indexerOpts, indexerName) + } + + indexer, err := factory(optsMap, resources) + if err != nil { + return appdata.Listener{}, fmt.Errorf("failed to create indexer %s: %w", indexerName, err) + } + + res, err := indexer.Initialize(ctx, InitializationData{}) + if err != nil { + return appdata.Listener{}, fmt.Errorf("failed to initialize indexer %s: %w", indexerName, err) + } + + indexers = append(indexers, res.Listener) + + // TODO handle last block persisted + } + + return decoding.Middleware(appdata.AsyncListenerMux(indexers, 1024, ctx.Done()), decoding.Options{ + DecoderResolver: opts.Resolver, + SyncSource: opts.SyncSource, + Logger: opts.Logger, + }) +} diff --git a/schema/log/logger.go b/schema/log/logger.go new file mode 100644 index 000000000000..2007d6cc5cc0 --- /dev/null +++ b/schema/log/logger.go @@ -0,0 +1,31 @@ +package log + +type Logger interface { + // Info takes a message and a set of key/value pairs and logs with level INFO. + // The key of the tuple must be a string. + Info(msg string, keyVals ...interface{}) + + // Warn takes a message and a set of key/value pairs and logs with level WARN. + // The key of the tuple must be a string. + Warn(msg string, keyVals ...interface{}) + + // Error takes a message and a set of key/value pairs and logs with level ERR. + // The key of the tuple must be a string. + Error(msg string, keyVals ...interface{}) + + // Debug takes a message and a set of key/value pairs and logs with level DEBUG. + // The key of the tuple must be a string. + Debug(msg string, keyVals ...interface{}) +} + +type NoopLogger struct{} + +func (n NoopLogger) Info(msg string, keyVals ...interface{}) {} + +func (n NoopLogger) Warn(msg string, keyVals ...interface{}) {} + +func (n NoopLogger) Error(msg string, keyVals ...interface{}) {} + +func (n NoopLogger) Debug(msg string, keyVals ...interface{}) {} + +var _ Logger = NoopLogger{} diff --git a/schema/object_type.go b/schema/object_type.go index 4189f88de82d..4ccc3c2d303f 100644 --- a/schema/object_type.go +++ b/schema/object_type.go @@ -25,6 +25,8 @@ type ObjectType struct { // though it is still valid in order to save space. Indexers will want to have // the option of retaining such data and distinguishing from other "true" deletions. RetainDeletions bool + + UniqueConstraints []UniqueConstraint } // Validate validates the object type. @@ -50,6 +52,10 @@ func (o ObjectType) validate(enumValueMap map[string]map[string]bool) error { return fmt.Errorf("key field %q cannot be nullable", field.Name) } + if field.Nullable { + return fmt.Errorf("key field %q cannot be nullable", field.Name) + } + if fieldNames[field.Name] { return fmt.Errorf("duplicate field name %q", field.Name) } diff --git a/schema/testing/CHANGELOG.md b/schema/testing/CHANGELOG.md new file mode 100644 index 000000000000..0c3c9d03857f --- /dev/null +++ b/schema/testing/CHANGELOG.md @@ -0,0 +1,37 @@ + + +# Changelog + +## [Unreleased] diff --git a/schema/testing/README.md b/schema/testing/README.md new file mode 100644 index 000000000000..5b89dcc2c685 --- /dev/null +++ b/schema/testing/README.md @@ -0,0 +1,4 @@ +# Schema Testing + +This module contains core test utilities and fixtures for testing schema and indexer functionality. It is managed as a separate go module to manage versions better and allow for dependencies on useful testing libraries without imposing those +elsewhere. \ No newline at end of file diff --git a/schema/testing/app.go b/schema/testing/app.go new file mode 100644 index 000000000000..56f50e03eab4 --- /dev/null +++ b/schema/testing/app.go @@ -0,0 +1,20 @@ +package schematesting + +import ( + "fmt" + + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +var AppSchemaGen = rapid.Custom(func(t *rapid.T) map[string]schema.ModuleSchema { + schema := make(map[string]schema.ModuleSchema) + numModules := rapid.IntRange(1, 10).Draw(t, "numModules") + for i := 0; i < numModules; i++ { + moduleName := NameGen.Draw(t, "moduleName") + moduleSchema := ModuleSchemaGen.Draw(t, fmt.Sprintf("moduleSchema[%s]", moduleName)) + schema[moduleName] = moduleSchema + } + return schema +}) diff --git a/schema/testing/app_test.go b/schema/testing/app_test.go new file mode 100644 index 000000000000..ac2600e238f2 --- /dev/null +++ b/schema/testing/app_test.go @@ -0,0 +1,18 @@ +package schematesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +func TestAppSchema(t *testing.T) { + rapid.Check(t, func(t *rapid.T) { + schema := AppSchemaGen.Draw(t, "schema") + for moduleName, moduleSchema := range schema { + require.NotEmpty(t, moduleName) + require.NoError(t, moduleSchema.Validate()) + } + }) +} diff --git a/schema/testing/appdata/app_data.go b/schema/testing/appdata/app_data.go new file mode 100644 index 000000000000..1c53f78716c9 --- /dev/null +++ b/schema/testing/appdata/app_data.go @@ -0,0 +1,131 @@ +package appdatatest + +import ( + "fmt" + + "pgregory.net/rapid" + + "cosmossdk.io/schema" + "cosmossdk.io/schema/appdata" + schematesting "cosmossdk.io/schema/testing" + "cosmossdk.io/schema/testing/statesim" +) + +type SimulatorOptions struct { + AppSchema map[string]schema.ModuleSchema + Listener appdata.Listener + EventAlignedWrites bool + StateSimOptions statesim.Options + StartBlockDataGen *rapid.Generator[appdata.StartBlockData] + TxDataGen *rapid.Generator[appdata.TxData] + EventDataGen *rapid.Generator[appdata.EventData] +} + +type Simulator struct { + state *statesim.App + options SimulatorOptions + blockNum uint64 + blockDataGen *rapid.Generator[BlockData] +} + +type BlockData = []appdata.Packet + +func NewSimulator(options SimulatorOptions) *Simulator { + if options.AppSchema == nil { + options.AppSchema = schematesting.ExampleAppSchema + } + + sim := &Simulator{ + state: statesim.NewApp(options.AppSchema, options.StateSimOptions), + options: options, + } + + return sim +} + +func (a *Simulator) Initialize() error { + if f := a.options.Listener.InitializeModuleData; f != nil { + err := a.state.ScanModuleSchemas(func(moduleName string, moduleSchema schema.ModuleSchema) error { + return f(appdata.ModuleInitializationData{ModuleName: moduleName, Schema: moduleSchema}) + }) + if err != nil { + return err + } + } + + return nil +} + +func (a *Simulator) BlockDataGen() *rapid.Generator[BlockData] { + return a.BlockDataGenN(100) +} + +func (a *Simulator) BlockDataGenN(maxUpdatesPerBlock int) *rapid.Generator[BlockData] { + numUpdatesGen := rapid.IntRange(1, maxUpdatesPerBlock) + + return rapid.Custom(func(t *rapid.T) BlockData { + var packets BlockData + + updateSet := map[string]bool{} + // filter out any updates to the same key from this block, otherwise we can end up with weird errors + updateGen := a.state.UpdateGen().Filter(func(data appdata.ObjectUpdateData) bool { + for _, update := range data.Updates { + _, existing := updateSet[fmt.Sprintf("%s:%v", data.ModuleName, update.Key)] + if existing { + return false + } + } + return true + }) + numUpdates := numUpdatesGen.Draw(t, "numUpdates") + for i := 0; i < numUpdates; i++ { + data := updateGen.Draw(t, fmt.Sprintf("update[%d]", i)) + for _, update := range data.Updates { + updateSet[fmt.Sprintf("%s:%v", data.ModuleName, update.Key)] = true + } + packets = append(packets, data) + } + + return packets + }) +} + +func (a *Simulator) ProcessBlockData(data BlockData) error { + a.blockNum++ + + if f := a.options.Listener.StartBlock; f != nil { + err := f(appdata.StartBlockData{Height: a.blockNum}) + if err != nil { + return err + } + } + + for _, packet := range data { + err := a.options.Listener.SendPacket(packet) + if err != nil { + return err + } + + if updateData, ok := packet.(appdata.ObjectUpdateData); ok { + for _, update := range updateData.Updates { + err = a.state.ApplyUpdate(updateData.ModuleName, update) + if err != nil { + return err + } + } + } + } + + if f := a.options.Listener.Commit; f != nil { + err := f(appdata.CommitData{}) + if err != nil { + return err + } + } + + return nil +} + +func (a *Simulator) AppState() *statesim.App { + return a.state +} diff --git a/schema/testing/appdata/app_data_test.go b/schema/testing/appdata/app_data_test.go new file mode 100644 index 000000000000..3a43e917a8e5 --- /dev/null +++ b/schema/testing/appdata/app_data_test.go @@ -0,0 +1,30 @@ +package appdatatest + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + "gotest.tools/v3/golden" + + "cosmossdk.io/schema/testing" +) + +func TestAppSimulator_ExampleSchema(t *testing.T) { + out := &bytes.Buffer{} + appSim := NewSimulator(SimulatorOptions{ + AppSchema: schematesting.ExampleAppSchema, + Listener: WriterListener(out), + }) + + require.NoError(t, appSim.Initialize()) + + blockDataGen := appSim.BlockDataGen() + + for i := 0; i < 10; i++ { + data := blockDataGen.Example(i + 1) + require.NoError(t, appSim.ProcessBlockData(data)) + } + + golden.Assert(t, out.String(), "app_sim_example_schema.txt") +} diff --git a/schema/testing/appdata/json.go b/schema/testing/appdata/json.go new file mode 100644 index 000000000000..379bfee8faf3 --- /dev/null +++ b/schema/testing/appdata/json.go @@ -0,0 +1,40 @@ +package appdatatest + +import ( + "pgregory.net/rapid" + + "cosmossdk.io/schema/appdata" +) + +func jsonValueGen() *rapid.Generator[any] { + return rapid.OneOf( + rapid.Bool().AsAny(), + rapid.Float64().AsAny(), + rapid.String().AsAny(), + rapid.MapOf(rapid.String(), rapid.Deferred(jsonValueGen)).AsAny(), + rapid.SliceOf(rapid.Deferred(jsonValueGen)).AsAny(), + ) +} + +var JSONValueGen = jsonValueGen() + +var JSONObjectGen = rapid.MapOf(rapid.String(), JSONValueGen) + +var JSONArrayGen = rapid.SliceOf(JSONValueGen) + +func JSONObjectWithKeys(keys ...string) *rapid.Generator[map[string]interface{}] { + return rapid.MapOf(rapid.SampledFrom(keys), JSONValueGen) +} + +func StringMapWithKeys(keys ...string) *rapid.Generator[map[string]string] { + return rapid.MapOf(rapid.SampledFrom(keys), rapid.String()) +} + +// events can consist of names separated by dots, e.g. "message.sent" +const eventTypeFormat = `^([a-zA-Z_][a-zA-Z0-9_]*\.)*[A-Za-z_][A-Za-z0-9_]$` + +var DefaultEventDataGen = rapid.Custom(func(t *rapid.T) appdata.EventData { + return appdata.EventData{ + Type: rapid.StringMatching(`^$`).Draw(t, "type"), + } +}) diff --git a/schema/testing/appdata/testdata/app_sim_example_schema.txt b/schema/testing/appdata/testdata/app_sim_example_schema.txt new file mode 100644 index 000000000000..95281780f917 --- /dev/null +++ b/schema/testing/appdata/testdata/app_sim_example_schema.txt @@ -0,0 +1,91 @@ +InitializeModuleSchema: all_kinds {"ObjectTypes":[{"Name":"test_string","KeyFields":[{"Name":"keyNotNull","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":1,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":1,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_bytes","KeyFields":[{"Name":"keyNotNull","Kind":2,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":2,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":2,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":2,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_int8","KeyFields":[{"Name":"keyNotNull","Kind":3,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":3,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":3,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":3,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_uint8","KeyFields":[{"Name":"keyNotNull","Kind":4,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":4,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":4,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":4,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_int16","KeyFields":[{"Name":"keyNotNull","Kind":5,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":5,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":5,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":5,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_uint16","KeyFields":[{"Name":"keyNotNull","Kind":6,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":6,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":6,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":6,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_int32","KeyFields":[{"Name":"keyNotNull","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":7,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":7,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_uint32","KeyFields":[{"Name":"keyNotNull","Kind":8,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":8,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":8,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":8,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_int64","KeyFields":[{"Name":"keyNotNull","Kind":9,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":9,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":9,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":9,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_uint64","KeyFields":[{"Name":"keyNotNull","Kind":10,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":10,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":10,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":10,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_integer","KeyFields":[{"Name":"keyNotNull","Kind":11,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":11,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":11,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":11,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_decimal","KeyFields":[{"Name":"keyNotNull","Kind":12,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":12,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":12,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":12,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_bool","KeyFields":[{"Name":"keyNotNull","Kind":13,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":13,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":13,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":13,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_time","KeyFields":[{"Name":"keyNotNull","Kind":14,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":14,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":14,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":14,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_duration","KeyFields":[{"Name":"keyNotNull","Kind":15,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":15,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":15,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":15,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_float32","KeyFields":[{"Name":"keyNotNull","Kind":16,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":16,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":16,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":16,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_float64","KeyFields":[{"Name":"keyNotNull","Kind":17,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":17,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":17,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":17,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_bech32address","KeyFields":[{"Name":"keyNotNull","Kind":18,"Nullable":false,"AddressPrefix":"cosmos","EnumDefinition":{"Name":"","Values":null}},{"Name":"keyNullable","Kind":18,"Nullable":true,"AddressPrefix":"cosmos","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"valNotNull","Kind":18,"Nullable":false,"AddressPrefix":"cosmos","EnumDefinition":{"Name":"","Values":null}},{"Name":"valNullable","Kind":18,"Nullable":true,"AddressPrefix":"cosmos","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"test_enum","KeyFields":[{"Name":"keyNotNull","Kind":19,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"test_enum","Values":["foo","bar","baz"]}},{"Name":"keyNullable","Kind":19,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"test_enum","Values":["foo","bar","baz"]}}],"ValueFields":[{"Name":"valNotNull","Kind":19,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"test_enum","Values":["foo","bar","baz"]}},{"Name":"valNullable","Kind":19,"Nullable":true,"AddressPrefix":"","EnumDefinition":{"Name":"test_enum","Values":["foo","bar","baz"]}}],"RetainDeletions":false}]} +InitializeModuleSchema: test_cases {"ObjectTypes":[{"Name":"Singleton","KeyFields":[],"ValueFields":[{"Name":"Value","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Value2","Kind":2,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"Simple","KeyFields":[{"Name":"Key","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"Value1","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Value2","Kind":2,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"Two Keys","KeyFields":[{"Name":"Key1","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Key2","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":null,"RetainDeletions":false},{"Name":"Three Keys","KeyFields":[{"Name":"Key1","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Key2","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Key3","Kind":10,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"Value1","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"Many Values","KeyFields":[{"Name":"Key","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"Value1","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Value2","Kind":2,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Value3","Kind":17,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Value4","Kind":10,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":false},{"Name":"RetainDeletions","KeyFields":[{"Name":"Key","Kind":1,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"ValueFields":[{"Name":"Value1","Kind":7,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}},{"Name":"Value2","Kind":2,"Nullable":false,"AddressPrefix":"","EnumDefinition":{"Name":"","Values":null}}],"RetainDeletions":true}]} +Initialize: {false} +StartBlock: 1 +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"a⃝?\rA�֍","Value":{"Value1":-2147483648,"Value2":"5j6jAR12ASgIHQMBA54DACYFGgI=","Value3":1817374971955183.2},"Delete":false} +Commit +StartBlock: 2 +OnObjectUpdate: all_kinds: {"TypeName":"test_bech32address","Key":["APo3CwC1wwTmagHIAorBdQoBAQMA0woAITL/GQH6tkT/AQE=",null],"Value":["/Mt59F4hjgGqAQ0B/wWlAqn2UqEAAQIAAU8MAYoCDzs3AgJoAAE/FgNLwAnnDcIeAAUYNS1Ytg==","ActiKPrSHCMDEjMMlg04AFflAz0qxAaWAASFAwkAywIGBxQAzF8xJQ=="],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bech32address","Key":["HDUI+gAHAhpSCu4LYgAB1wMAtQAEBctXUQ==",null],"Value":{"valNullable":"BwERCwQtBAMI8Br2Awafjke1kAEDAQMA//ssKgTpMgYMAAMBB+oUGAACBBoALwcePkwA//8pHQCtTvylLIXt8A=="},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"a⃝?\rA�֍","Value":{"Value1":197503,"Value4":4093396},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_duration","Key":[-322686,-3],"Value":[4067136,null],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_int8","Key":[6,0],"Value":{"valNotNull":-59},"Delete":false} +Commit +StartBlock: 3 +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[true,null],"Value":{},"Delete":false} +Commit +StartBlock: 4 +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"a⃝?\rA�֍","Value":{"Value1":-1,"Value2":"3CsGAls=","Value4":58783},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_string","Key":["A^'","*?{a"],"Value":{"valNullable":"‮⋁"},"Delete":false} +Commit +StartBlock: 5 +OnObjectUpdate: all_kinds: {"TypeName":"test_bech32address","Key":["APo3CwC1wwTmagHIAorBdQoBAQMA0woAITL/GQH6tkT/AQE=",null],"Value":{"valNotNull":"OgOQG+lGAgF5Y+EAAwAAAkQHATYDYXUB","valNullable":"AQGXARQPGgE7AgHIDYoAACEBC/6MyDYLuADecgDPACvqAAELpGkACwAAqOIJAgEDKw=="},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"","Value":[-8564477,"AcQ="],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_duration","Key":[-1,null],"Value":{},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"\t𓐸\u0026A","Value":[-11084,"",5.3864324145915995e+81,285497682],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"\u0026A","Value":{"Value2":"Ab/p"},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"","Value":{},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Two Keys","Key":["؁〩a_𞥟",-120667635],"Value":null,"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[false,null],"Value":[true,null],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_int8","Key":[-4,6],"Value":{},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Singleton","Key":null,"Value":{},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"\u0026`aᾼa\u0000#ᛮ𞥞aᾮ\u0026₰?𐧀~ ा၉𩮘A` ","Value":{"Value1":163,"Value2":"iwGmHAAWaAIPz6U="},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"𝞒𐄍#?ᾫ","Value":{"Value1":246341895},"Delete":false} +Commit +StartBlock: 6 +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"\u0026`aᾼa\u0000#ᛮ𞥞aᾮ\u0026₰?𐧀~ ा၉𩮘A` ","Value":null,"Delete":true} +OnObjectUpdate: all_kinds: {"TypeName":"test_enum","Key":["bar","foo"],"Value":["bar",null],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_enum","Key":["foo","bar"],"Value":{},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bytes","Key":["BAAZ/Mo=","C7U="],"Value":{},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"Ƶa\u0026[.\u0026a.","Value":[-314,"AA0LT19MujQyf/8FbQMDawAM"],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_enum","Key":["bar","foo"],"Value":{"valNullable":"foo"},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[true,null],"Value":{"valNotNull":true,"valNullable":null},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"_$A\r","Value":{"Value1":1319133},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"","Value":[-14,"BA=="],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"Ƶa\u0026[.\u0026a.","Value":null,"Delete":true} +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"g$🄛\u0026#?","Value":{"Value1":2,"Value2":"BvIE"},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Singleton","Key":null,"Value":null,"Delete":true} +Commit +StartBlock: 7 +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"","Value":[-4,"nAABEGi4pg=="],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"aᱻꞘ","Value":[-11,"CA=="],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"","Value":{"Value1":-61,"Value4":4},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"+","Value":[-1891215,"+UwDKRIC"],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bech32address","Key":["NAHCtAMbAQEHTAI3DgcfG8hLASMq4DICAj41AAubARsBATQg3gCppQMAAQwHASEDAwD+55ELSKjFAC4oGwgKBA==","nyYBB4AGAJnxe792TCHxAg5qHS4A"],"Value":["AG2pEQEGAQFzIAFWAQEACv8CAgECgP8AtA==",null],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Two Keys","Key":["!฿*$\u0026AȺ#˼%\u0000ⅷ_ŕ,A",-467],"Value":null,"Delete":false} +Commit +StartBlock: 8 +OnObjectUpdate: all_kinds: {"TypeName":"test_float32","Key":[-11851129000000000000,-2014.5674],"Value":[-1.0672615e-18,2.771192],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"\u0026A","Value":[3901734,"AwMF"],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"Aः𒑨Dz؅","Value":[0,""],"Delete":false} +Commit +StartBlock: 9 +OnObjectUpdate: all_kinds: {"TypeName":"test_int64","Key":[-4,5],"Value":[100,null],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bytes","Key":["/w==",null],"Value":["IgACFRoAfgMDBMi/",null],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[true,null],"Value":null,"Delete":true} +OnObjectUpdate: test_cases: {"TypeName":"RetainDeletions","Key":"","Value":{"Value2":"BgEHAAUG0QgDAw=="},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bech32address","Key":["HDUI+gAHAhpSCu4LYgAB1wMAtQAEBctXUQ==",null],"Value":null,"Delete":true} +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[false,null],"Value":[true,null],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bytes","Key":["/w==",null],"Value":["NQE=",null],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"\t𓐸\u0026A","Value":null,"Delete":true} +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[false,true],"Value":[false,null],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"","Value":{"Value3":5984325148376916},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_uint16","Key":[0,null],"Value":[43,0],"Delete":false} +Commit +StartBlock: 10 +OnObjectUpdate: test_cases: {"TypeName":"Simple","Key":"\\༸՚@","Value":[6,"AA=="],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_decimal","Key":["31940815527640952","2665097019"],"Value":["930126","5301e322"],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_int16","Key":[-8610,null],"Value":[1195,-25804],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[true,null],"Value":[false,true],"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"","Value":[-1,"AwFIAwmRCA==",5.463114807757741e-9,2],"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_bool","Key":[false,false],"Value":{"valNotNull":true},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_decimal","Key":["82509790016910",null],"Value":{"valNotNull":"-2","valNullable":null},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Singleton","Key":null,"Value":{},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_int64","Key":[-4,5],"Value":null,"Delete":true} +OnObjectUpdate: all_kinds: {"TypeName":"test_bech32address","Key":["NAHCtAMbAQEHTAI3DgcfG8hLASMq4DICAj41AAubARsBATQg3gCppQMAAQwHASEDAwD+55ELSKjFAC4oGwgKBA==","nyYBB4AGAJnxe792TCHxAg5qHS4A"],"Value":null,"Delete":true} +OnObjectUpdate: all_kinds: {"TypeName":"test_bech32address","Key":["ZPpWX+smAAJmAKAc//8KTwAuXQAUHKICBwEBFFB6Bx8IVh0kugBLAVSVFhYDtDULkwIwAYwA+gfMA6k=",null],"Value":{},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Many Values","Key":"","Value":{"Value1":847967},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_decimal","Key":["50.676",null],"Value":{"valNotNull":"-11515688332E35"},"Delete":false} +OnObjectUpdate: all_kinds: {"TypeName":"test_enum","Key":["bar","foo"],"Value":{"valNotNull":"bar"},"Delete":false} +OnObjectUpdate: test_cases: {"TypeName":"Two Keys","Key":["!฿*$\u0026AȺ#˼%\u0000ⅷ_ŕ,A",-467],"Value":null,"Delete":false} +Commit diff --git a/schema/testing/appdata/write_listener.go b/schema/testing/appdata/write_listener.go new file mode 100644 index 000000000000..41a1dcb24043 --- /dev/null +++ b/schema/testing/appdata/write_listener.go @@ -0,0 +1,41 @@ +package appdatatest + +import ( + "encoding/json" + "fmt" + "io" + + "cosmossdk.io/schema/appdata" +) + +func WriterListener(w io.Writer) appdata.Listener { + return appdata.Listener{ + StartBlock: func(data appdata.StartBlockData) error { + _, err := fmt.Fprintf(w, "StartBlock: %v\n", data) + return err + }, + OnTx: nil, + OnEvent: nil, + OnKVPair: nil, + Commit: func(data appdata.CommitData) error { + _, err := fmt.Fprintf(w, "Commit: %v\n", data) + return err + }, + InitializeModuleData: func(data appdata.ModuleInitializationData) error { + bz, err := json.Marshal(data) + if err != nil { + return err + } + _, err = fmt.Fprintf(w, "InitializeModuleData: %s\n", bz) + return err + }, + OnObjectUpdate: func(data appdata.ObjectUpdateData) error { + bz, err := json.Marshal(data) + if err != nil { + return err + } + _, err = fmt.Fprintf(w, "OnObjectUpdate: %s\n", bz) + return err + }, + } +} diff --git a/schema/testing/enum.go b/schema/testing/enum.go new file mode 100644 index 000000000000..f4aee77c9dba --- /dev/null +++ b/schema/testing/enum.go @@ -0,0 +1,18 @@ +package schematesting + +import ( + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +var enumValuesGen = rapid.SliceOfNDistinct(NameGen, 1, 10, func(x string) string { return x }) + +var EnumDefinitionGen = rapid.Custom(func(t *rapid.T) schema.EnumDefinition { + enum := schema.EnumDefinition{ + Name: NameGen.Draw(t, "name"), + Values: enumValuesGen.Draw(t, "values"), + } + + return enum +}) diff --git a/schema/testing/enum_test.go b/schema/testing/enum_test.go new file mode 100644 index 000000000000..10d87db9e65d --- /dev/null +++ b/schema/testing/enum_test.go @@ -0,0 +1,15 @@ +package schematesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +func TestEnumDefinition(t *testing.T) { + rapid.Check(t, func(t *rapid.T) { + enumDefinition := EnumDefinitionGen.Draw(t, "enum") + require.NoError(t, enumDefinition.Validate()) + }) +} diff --git a/schema/testing/example_schema.go b/schema/testing/example_schema.go new file mode 100644 index 000000000000..77d1e66c6879 --- /dev/null +++ b/schema/testing/example_schema.go @@ -0,0 +1,198 @@ +package schematesting + +import ( + "fmt" + + "cosmossdk.io/schema" +) + +var ExampleAppSchema = map[string]schema.ModuleSchema{ + "all_kinds": mkAllKindsModule(), + "test_cases": { + ObjectTypes: []schema.ObjectType{ + { + Name: "Singleton", + KeyFields: []schema.Field{}, + ValueFields: []schema.Field{ + { + Name: "Value", + Kind: schema.StringKind, + }, + { + Name: "Value2", + Kind: schema.BytesKind, + }, + }, + }, + { + Name: "Simple", + KeyFields: []schema.Field{ + { + Name: "Key", + Kind: schema.StringKind, + }, + }, + ValueFields: []schema.Field{ + { + Name: "Value1", + Kind: schema.Int32Kind, + }, + { + Name: "Value2", + Kind: schema.BytesKind, + }, + }, + }, + { + Name: "TwoKeys", + KeyFields: []schema.Field{ + { + Name: "Key1", + Kind: schema.StringKind, + }, + { + Name: "Key2", + Kind: schema.Int32Kind, + }, + }, + }, + { + Name: "ThreeKeys", + KeyFields: []schema.Field{ + { + Name: "Key1", + Kind: schema.StringKind, + }, + { + Name: "Key2", + Kind: schema.Int32Kind, + }, + { + Name: "Key3", + Kind: schema.Uint64Kind, + }, + }, + ValueFields: []schema.Field{ + { + Name: "Value1", + Kind: schema.Int32Kind, + }, + }, + }, + { + Name: "ManyValues", + KeyFields: []schema.Field{ + { + Name: "Key", + Kind: schema.StringKind, + }, + }, + ValueFields: []schema.Field{ + { + Name: "Value1", + Kind: schema.Int32Kind, + }, + { + Name: "Value2", + Kind: schema.BytesKind, + }, + { + Name: "Value3", + Kind: schema.Float64Kind, + }, + { + Name: "Value4", + Kind: schema.Uint64Kind, + }, + }, + }, + { + Name: "RetainDeletions", + KeyFields: []schema.Field{ + { + Name: "Key", + Kind: schema.StringKind, + }, + }, + ValueFields: []schema.Field{ + { + Name: "Value1", + Kind: schema.Int32Kind, + }, + { + Name: "Value2", + Kind: schema.BytesKind, + }, + }, + RetainDeletions: true, + }, + { + Name: "UniqueConstraint", + KeyFields: []schema.Field{ + { + Name: "Key", + Kind: schema.StringKind, + }, + }, + ValueFields: []schema.Field{ + { + Name: "Value1", + Kind: schema.Int32Kind, + }, + { + Name: "Value2", + Kind: schema.BytesKind, + }, + }, + UniqueConstraints: []schema.UniqueConstraint{ + {[]string{"Value1"}}, + }, + }, + }, + }, +} + +func mkAllKindsModule() schema.ModuleSchema { + mod := schema.ModuleSchema{} + + for i := 1; i < int(schema.MAX_VALID_KIND); i++ { + kind := schema.Kind(i) + typ := mkTestObjectType(kind) + mod.ObjectTypes = append(mod.ObjectTypes, typ) + } + + return mod +} + +func mkTestObjectType(kind schema.Kind) schema.ObjectType { + field := schema.Field{ + Kind: kind, + } + + if kind == schema.EnumKind { + field.EnumDefinition = testEnum + } + + if kind == schema.Bech32AddressKind { + field.AddressPrefix = "cosmos" + } + + keyField := field + keyField.Name = "key" + val1Field := field + val1Field.Name = "valNotNull" + val2Field := field + val2Field.Name = "valNullable" + val2Field.Nullable = true + + return schema.ObjectType{ + Name: fmt.Sprintf("test_%v", kind), + KeyFields: []schema.Field{keyField}, + ValueFields: []schema.Field{val1Field, val2Field}, + } +} + +var testEnum = schema.EnumDefinition{ + Name: "test_enum_type", + Values: []string{"foo", "bar", "baz"}, +} diff --git a/schema/testing/field.go b/schema/testing/field.go new file mode 100644 index 000000000000..8803e4b3150f --- /dev/null +++ b/schema/testing/field.go @@ -0,0 +1,168 @@ +package schematesting + +import ( + "fmt" + "time" + + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +var ( + kindGen = rapid.Map(rapid.IntRange(int(schema.InvalidKind+1), int(schema.MAX_VALID_KIND-1)), + func(i int) schema.Kind { + return schema.Kind(i) + }) + boolGen = rapid.Bool() +) + +var FieldGen = rapid.Custom(func(t *rapid.T) schema.Field { + kind := kindGen.Draw(t, "kind") + field := schema.Field{ + Name: NameGen.Draw(t, "name"), + Kind: kind, + Nullable: boolGen.Draw(t, "nullable"), + } + + switch kind { + case schema.EnumKind: + field.EnumDefinition = EnumDefinitionGen.Draw(t, "enumDefinition") + case schema.Bech32AddressKind: + field.AddressPrefix = NameGen.Draw(t, "addressPrefix") + default: + } + + return field +}) + +func FieldValueGen(field schema.Field) *rapid.Generator[any] { + gen := baseFieldValue(field) + + if field.Nullable { + return rapid.OneOf(gen, rapid.Just[any](nil)).AsAny() + } + + return gen +} + +func baseFieldValue(field schema.Field) *rapid.Generator[any] { + switch field.Kind { + case schema.StringKind: + return rapid.StringOf(rapid.Rune().Filter(func(r rune) bool { + return r != 0 // filter out NULL characters + })).AsAny() + case schema.BytesKind: + return rapid.SliceOf(rapid.Byte()).AsAny() + case schema.Int8Kind: + return rapid.Int8().AsAny() + case schema.Int16Kind: + return rapid.Int16().AsAny() + case schema.Uint8Kind: + return rapid.Uint8().AsAny() + case schema.Uint16Kind: + return rapid.Uint16().AsAny() + case schema.Int32Kind: + return rapid.Int32().AsAny() + case schema.Uint32Kind: + return rapid.Uint32().AsAny() + case schema.Int64Kind: + return rapid.Int64().AsAny() + case schema.Uint64Kind: + return rapid.Uint64().AsAny() + case schema.Float32Kind: + return rapid.Float32().AsAny() + case schema.Float64Kind: + return rapid.Float64().AsAny() + case schema.IntegerStringKind: + return rapid.StringMatching(schema.IntegerFormat).AsAny() + case schema.DecimalStringKind: + return rapid.StringMatching(schema.DecimalFormat).AsAny() + case schema.BoolKind: + return rapid.Bool().AsAny() + case schema.TimeKind: + return rapid.Map(rapid.Int64(), func(i int64) time.Time { + return time.Unix(0, i) + }).AsAny() + case schema.DurationKind: + return rapid.Map(rapid.Int64(), func(i int64) time.Duration { + return time.Duration(i) + }).AsAny() + case schema.Bech32AddressKind: + return rapid.SliceOfN(rapid.Byte(), 20, 64).AsAny() + case schema.EnumKind: + return rapid.SampledFrom(field.EnumDefinition.Values).AsAny() + default: + panic(fmt.Errorf("unexpected kind: %v", field.Kind)) + } +} + +func KeyFieldsValueGen(keyFields []schema.Field) *rapid.Generator[any] { + if len(keyFields) == 0 { + return rapid.Just[any](nil) + } + + if len(keyFields) == 1 { + return FieldValueGen(keyFields[0]) + } + + gens := make([]*rapid.Generator[any], len(keyFields)) + for i, field := range keyFields { + gens[i] = FieldValueGen(field) + } + + return rapid.Custom(func(t *rapid.T) any { + values := make([]any, len(keyFields)) + for i, gen := range gens { + values[i] = gen.Draw(t, keyFields[i].Name) + } + return values + }) +} + +func ValueFieldsValueGen(valueFields []schema.Field, forUpdate bool) *rapid.Generator[any] { + // special case where there are no value fields + // we shouldn't end up here, but just in case + if len(valueFields) == 0 { + return rapid.Just[any](nil) + } + + gens := make([]*rapid.Generator[any], len(valueFields)) + for i, field := range valueFields { + gens[i] = FieldValueGen(field) + } + return rapid.Custom(func(t *rapid.T) any { + // return ValueUpdates 50% of the time + if boolGen.Draw(t, "valueUpdates") { + updates := map[string]any{} + + n := len(valueFields) + for i, gen := range gens { + lastField := i == n-1 + haveUpdates := len(updates) > 0 + // skip 50% of the time if this is an update + // but check if we have updates by the time we reach the last field + // so we don't have an empty update + if forUpdate && + (!lastField || haveUpdates) && + boolGen.Draw(t, fmt.Sprintf("skip_%s", valueFields[i].Name)) { + continue + } + updates[valueFields[i].Name] = gen.Draw(t, valueFields[i].Name) + } + + return schema.MapValueUpdates(updates) + } else { + if len(valueFields) == 1 { + return gens[0].Draw(t, valueFields[0].Name) + } + + values := make([]any, len(valueFields)) + for i, gen := range gens { + values[i] = gen.Draw(t, valueFields[i].Name) + } + + return values + } + }) +} diff --git a/schema/testing/field_test.go b/schema/testing/field_test.go new file mode 100644 index 000000000000..2c78ff906372 --- /dev/null +++ b/schema/testing/field_test.go @@ -0,0 +1,37 @@ +package schematesting + +import ( + "testing" + "unicode/utf8" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +func TestField(t *testing.T) { + rapid.Check(t, func(t *rapid.T) { + field := FieldGen.Draw(t, "field") + require.NoError(t, field.Validate()) + }) +} + +func TestFieldValue(t *testing.T) { + rapid.Check(t, checkFieldValue) +} + +func FuzzFieldValue(f *testing.F) { + strGen := rapid.String() + f.Fuzz(rapid.MakeFuzz(func(t *rapid.T) { + str := strGen.Draw(t, "str") + if !utf8.ValidString(str) { + t.Fatalf("invalid utf8 string: %q", str) + } + })) +} + +var checkFieldValue = func(t *rapid.T) { + field := FieldGen.Draw(t, "field") + require.NoError(t, field.Validate()) + fieldValue := FieldValueGen(field).Draw(t, "fieldValue") + require.NoError(t, field.ValidateValue(fieldValue)) +} diff --git a/schema/testing/go.mod b/schema/testing/go.mod new file mode 100644 index 000000000000..e1cfc343bcdf --- /dev/null +++ b/schema/testing/go.mod @@ -0,0 +1,20 @@ +module cosmossdk.io/schema/testing + +require ( + cosmossdk.io/schema v0.0.0 + github.com/stretchr/testify v1.9.0 + github.com/tidwall/btree v1.7.0 + gotest.tools/v3 v3.5.1 + pgregory.net/rapid v1.1.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace cosmossdk.io/schema => ./.. + +go 1.22 diff --git a/schema/testing/go.sum b/schema/testing/go.sum new file mode 100644 index 000000000000..393b537c2d4a --- /dev/null +++ b/schema/testing/go.sum @@ -0,0 +1,18 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/schema/testing/module_schema.go b/schema/testing/module_schema.go new file mode 100644 index 000000000000..ee36232acf2f --- /dev/null +++ b/schema/testing/module_schema.go @@ -0,0 +1,31 @@ +package schematesting + +import ( + "fmt" + + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +var ModuleSchemaGen = rapid.Custom(func(t *rapid.T) schema.ModuleSchema { + schema := schema.ModuleSchema{} + numObjectTypes := rapid.IntRange(1, 10).Draw(t, "numObjectTypes") + for i := 0; i < numObjectTypes; i++ { + objectType := ObjectTypeGen.Draw(t, fmt.Sprintf("objectType[%d]", i)) + schema.ObjectTypes = append(schema.ObjectTypes, objectType) + } + return schema +}).Filter(func(schema schema.ModuleSchema) bool { + // filter out enums with duplicate names + enumTypeNames := map[string]bool{} + for _, objectType := range schema.ObjectTypes { + if !checkDuplicateEnumName(enumTypeNames, objectType.KeyFields) { + return false + } + if !checkDuplicateEnumName(enumTypeNames, objectType.ValueFields) { + return false + } + } + return true +}) diff --git a/schema/testing/module_schema_test.go b/schema/testing/module_schema_test.go new file mode 100644 index 000000000000..91196d59aa18 --- /dev/null +++ b/schema/testing/module_schema_test.go @@ -0,0 +1,15 @@ +package schematesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +func TestModuleSchema(t *testing.T) { + rapid.Check(t, func(t *rapid.T) { + schema := ModuleSchemaGen.Draw(t, "schema") + require.NoError(t, schema.Validate()) + }) +} diff --git a/schema/testing/name.go b/schema/testing/name.go new file mode 100644 index 000000000000..89d21faccec8 --- /dev/null +++ b/schema/testing/name.go @@ -0,0 +1,9 @@ +package schematesting + +import ( + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +var NameGen = rapid.StringMatching(schema.NameFormat) diff --git a/schema/testing/name_test.go b/schema/testing/name_test.go new file mode 100644 index 000000000000..b4d9a44ea612 --- /dev/null +++ b/schema/testing/name_test.go @@ -0,0 +1,17 @@ +package schematesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +func TestName(t *testing.T) { + rapid.Check(t, func(t *rapid.T) { + name := NameGen.Draw(t, "name") + require.True(t, schema.ValidateName(name)) + }) +} diff --git a/schema/testing/object.go b/schema/testing/object.go new file mode 100644 index 000000000000..09e77052d0dd --- /dev/null +++ b/schema/testing/object.go @@ -0,0 +1,122 @@ +package schematesting + +import ( + "github.com/tidwall/btree" + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +var fieldsGen = rapid.SliceOfNDistinct(FieldGen, 1, 12, func(f schema.Field) string { + return f.Name +}) + +var ObjectTypeGen = rapid.Custom(func(t *rapid.T) schema.ObjectType { + typ := schema.ObjectType{ + Name: NameGen.Draw(t, "name"), + } + + fields := fieldsGen.Draw(t, "fields") + numKeyFields := rapid.IntRange(0, len(fields)).Draw(t, "numKeyFields") + + typ.KeyFields = fields[:numKeyFields] + + for i := range typ.KeyFields { + // key fields can't be nullable + typ.KeyFields[i].Nullable = false + } + + typ.ValueFields = fields[numKeyFields:] + + typ.RetainDeletions = boolGen.Draw(t, "retainDeletions") + + return typ +}).Filter(func(typ schema.ObjectType) bool { + // filter out duplicate enum names + enumTypeNames := map[string]bool{} + if !checkDuplicateEnumName(enumTypeNames, typ.KeyFields) { + return false + } + if !checkDuplicateEnumName(enumTypeNames, typ.ValueFields) { + return false + } + return true +}) + +func checkDuplicateEnumName(enumTypeNames map[string]bool, fields []schema.Field) bool { + for _, field := range fields { + if field.Kind != schema.EnumKind { + continue + } + + if _, ok := enumTypeNames[field.EnumDefinition.Name]; ok { + return false + } + + enumTypeNames[field.EnumDefinition.Name] = true + } + return true +} + +func ObjectInsertGen(objectType schema.ObjectType) *rapid.Generator[schema.ObjectUpdate] { + return ObjectUpdateGen(objectType, nil) +} + +func ObjectUpdateGen(objectType schema.ObjectType, state *btree.Map[string, schema.ObjectUpdate]) *rapid.Generator[schema.ObjectUpdate] { + keyGen := KeyFieldsValueGen(objectType.KeyFields) + + if len(objectType.ValueFields) == 0 { + // special case where there are no value fields, + // so we just insert or delete, no updates + return rapid.Custom(func(t *rapid.T) schema.ObjectUpdate { + update := schema.ObjectUpdate{ + TypeName: objectType.Name, + } + + // 50% of the time delete existing key (when there are keys) + n := 0 + if state != nil { + n = state.Len() + } + if n > 0 && boolGen.Draw(t, "delete") { + i := rapid.IntRange(0, n-1).Draw(t, "index") + update.Key = state.Values()[i].Key + update.Delete = true + } else { + update.Key = keyGen.Draw(t, "key") + } + + return update + }) + } else { + insertValueGen := ValueFieldsValueGen(objectType.ValueFields, false) + updateValueGen := ValueFieldsValueGen(objectType.ValueFields, true) + return rapid.Custom(func(t *rapid.T) schema.ObjectUpdate { + update := schema.ObjectUpdate{ + TypeName: objectType.Name, + } + + // 50% of the time use existing key (when there are keys) + n := 0 + if state != nil { + n = state.Len() + } + if n > 0 && boolGen.Draw(t, "existingKey") { + i := rapid.IntRange(0, n-1).Draw(t, "index") + update.Key = state.Values()[i].Key + + // delete 50% of the time + if boolGen.Draw(t, "delete") { + update.Delete = true + } else { + update.Value = updateValueGen.Draw(t, "value") + } + } else { + update.Key = keyGen.Draw(t, "key") + update.Value = insertValueGen.Draw(t, "value") + } + + return update + }) + } +} diff --git a/schema/testing/object_test.go b/schema/testing/object_test.go new file mode 100644 index 000000000000..629d8e4764b0 --- /dev/null +++ b/schema/testing/object_test.go @@ -0,0 +1,31 @@ +package schematesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +func TestObject(t *testing.T) { + rapid.Check(t, func(t *rapid.T) { + objectType := ObjectTypeGen.Draw(t, "object") + require.NoError(t, objectType.Validate()) + }) +} + +func TestObjectUpdate(t *testing.T) { + rapid.Check(t, func(t *rapid.T) { + objectType := ObjectTypeGen.Draw(t, "object") + require.NoError(t, objectType.Validate()) + update := ObjectInsertGen(objectType).Draw(t, "update") + require.NoError(t, objectType.ValidateObjectUpdate(update)) + }) +} + +func TestExample(t *testing.T) { + objectType := ObjectTypeGen.Example(1) + update := ObjectInsertGen(objectType).Example(2) + t.Logf("objectType: %+v", objectType) + t.Logf("update: %+v", update) +} diff --git a/schema/testing/statesim/app.go b/schema/testing/statesim/app.go new file mode 100644 index 000000000000..b242429e73f0 --- /dev/null +++ b/schema/testing/statesim/app.go @@ -0,0 +1,103 @@ +package statesim + +import ( + "fmt" + + "github.com/stretchr/testify/require" + "github.com/tidwall/btree" + "pgregory.net/rapid" + + "cosmossdk.io/schema" + "cosmossdk.io/schema/appdata" +) + +type App struct { + moduleStates *btree.Map[string, *Module] + updateGen *rapid.Generator[appdata.ObjectUpdateData] +} + +func NewApp(appSchema map[string]schema.ModuleSchema, options Options) *App { + moduleStates := &btree.Map[string, *Module]{} + var moduleNames []string + + for moduleName, moduleSchema := range appSchema { + moduleState := NewModule(moduleSchema, options) + moduleStates.Set(moduleName, moduleState) + moduleNames = append(moduleNames, moduleName) + } + + moduleNameSelector := rapid.Map(rapid.IntRange(0, len(moduleNames)), func(u int) string { + return moduleNames[u] + }) + + numUpdatesGen := rapid.IntRange(1, 2) + updateGen := rapid.Custom(func(t *rapid.T) appdata.ObjectUpdateData { + moduleName := moduleNameSelector.Draw(t, "moduleName") + moduleState, ok := moduleStates.Get(moduleName) + require.True(t, ok) + numUpdates := numUpdatesGen.Draw(t, "numUpdates") + updates := make([]schema.ObjectUpdate, numUpdates) + for i := 0; i < numUpdates; i++ { + update := moduleState.UpdateGen().Draw(t, fmt.Sprintf("update[%d]", i)) + updates[i] = update + } + return appdata.ObjectUpdateData{ + ModuleName: moduleName, + Updates: updates, + } + }) + + return &App{ + moduleStates: moduleStates, + updateGen: updateGen, + } +} + +func (a *App) ApplyUpdate(moduleName string, update schema.ObjectUpdate) error { + moduleState, ok := a.moduleStates.Get(moduleName) + if !ok { + return fmt.Errorf("module %s not found", moduleName) + } + + return moduleState.ApplyUpdate(update) +} + +func (a *App) UpdateGen() *rapid.Generator[appdata.ObjectUpdateData] { + return a.updateGen +} + +func (a *App) ScanModuleSchemas(f func(string, schema.ModuleSchema) error) error { + var err error + a.moduleStates.Scan(func(key string, value *Module) bool { + err = f(key, value.moduleSchema) + return err == nil + }) + return err +} + +func (a *App) GetModule(moduleName string) (*Module, bool) { + return a.moduleStates.Get(moduleName) +} + +func (a *App) ScanState(f func(moduleName string, update schema.ObjectUpdate) error) error { + var err error + a.moduleStates.Scan(func(moduleName string, value *Module) bool { + err = value.ScanState(func(update schema.ObjectUpdate) error { + return f(moduleName, update) + }) + return err == nil + }) + return err +} + +func (a *App) ScanObjectCollections(f func(moduleName string, collection *ObjectCollection) error) error { + var err error + a.moduleStates.Scan(func(moduleName string, value *Module) bool { + value.objectCollections.Scan(func(key string, value *ObjectCollection) bool { + err = f(moduleName, value) + return err == nil + }) + return err == nil + }) + return err +} diff --git a/schema/testing/statesim/module.go b/schema/testing/statesim/module.go new file mode 100644 index 000000000000..15c2f663ee69 --- /dev/null +++ b/schema/testing/statesim/module.go @@ -0,0 +1,72 @@ +package statesim + +import ( + "fmt" + + "github.com/stretchr/testify/require" + "github.com/tidwall/btree" + "pgregory.net/rapid" + + "cosmossdk.io/schema" +) + +type Module struct { + moduleSchema schema.ModuleSchema + objectCollections *btree.Map[string, *ObjectCollection] + updateGen *rapid.Generator[schema.ObjectUpdate] +} + +func NewModule(moduleSchema schema.ModuleSchema, options Options) *Module { + objectCollections := &btree.Map[string, *ObjectCollection]{} + var objectTypeNames []string + for _, objectType := range moduleSchema.ObjectTypes { + objectCollection := NewObjectCollection(objectType, options) + objectCollections.Set(objectType.Name, objectCollection) + objectTypeNames = append(objectTypeNames, objectType.Name) + } + + objectTypeSelector := rapid.Map(rapid.IntRange(0, len(objectTypeNames)), func(u int) string { + return objectTypeNames[u] + }) + + updateGen := rapid.Custom(func(t *rapid.T) schema.ObjectUpdate { + objectType := objectTypeSelector.Draw(t, "objectType") + objectColl, ok := objectCollections.Get(objectType) + require.True(t, ok) + return objectColl.UpdateGen().Draw(t, "update") + }) + + return &Module{ + moduleSchema: moduleSchema, + updateGen: updateGen, + objectCollections: objectCollections, + } +} + +func (o *Module) ApplyUpdate(update schema.ObjectUpdate) error { + objState, ok := o.objectCollections.Get(update.TypeName) + if !ok { + return fmt.Errorf("object type %s not found in module", update.TypeName) + } + + return objState.ApplyUpdate(update) +} + +func (o *Module) UpdateGen() *rapid.Generator[schema.ObjectUpdate] { + return o.updateGen +} + +func (o *Module) GetObjectCollection(objectType string) (*ObjectCollection, bool) { + return o.objectCollections.Get(objectType) +} + +func (o *Module) ScanState(f func(schema.ObjectUpdate) error) error { + var err error + o.objectCollections.Scan(func(key string, value *ObjectCollection) bool { + err = value.ScanState(func(update schema.ObjectUpdate) error { + return f(update) + }) + return err == nil + }) + return err +} diff --git a/schema/testing/statesim/object.go b/schema/testing/statesim/object.go new file mode 100644 index 000000000000..6b11361ea7ca --- /dev/null +++ b/schema/testing/statesim/object.go @@ -0,0 +1,117 @@ +package statesim + +import ( + "fmt" + + "github.com/tidwall/btree" + "pgregory.net/rapid" + + "cosmossdk.io/schema" + schematesting "cosmossdk.io/schema/testing" +) + +type ObjectCollection struct { + options Options + objectType schema.ObjectType + objects *btree.Map[string, schema.ObjectUpdate] + updateGen *rapid.Generator[schema.ObjectUpdate] + valueFieldIndices map[string]int +} + +func NewObjectCollection(objectType schema.ObjectType, options Options) *ObjectCollection { + objects := &btree.Map[string, schema.ObjectUpdate]{} + updateGen := schematesting.ObjectUpdateGen(objectType, objects) + valueFieldIndices := make(map[string]int, len(objectType.ValueFields)) + for i, field := range objectType.ValueFields { + valueFieldIndices[field.Name] = i + } + + return &ObjectCollection{ + options: options, + objectType: objectType, + objects: objects, + updateGen: updateGen, + valueFieldIndices: valueFieldIndices, + } +} + +func (o *ObjectCollection) ApplyUpdate(update schema.ObjectUpdate) error { + if update.TypeName != o.objectType.Name { + return fmt.Errorf("update type name %q does not match object type name %q", update.TypeName, o.objectType.Name) + } + + err := o.objectType.ValidateObjectUpdate(update) + if err != nil { + return err + } + + keyStr := fmt.Sprintf("%v", update.Key) + cur, exists := o.objects.Get(keyStr) + if update.Delete { + if o.objectType.RetainDeletions && o.options.CanRetainDeletions { + if !exists { + return fmt.Errorf("object not found for deletion: %v", update.Key) + } + + cur.Delete = true + o.objects.Set(keyStr, cur) + } else { + o.objects.Delete(keyStr) + } + } else { + // merge value updates only if we have more than one value field + if valueUpdates, ok := update.Value.(schema.ValueUpdates); ok && + len(o.objectType.ValueFields) > 1 { + var values []interface{} + if exists { + values = cur.Value.([]interface{}) + } else { + values = make([]interface{}, len(o.objectType.ValueFields)) + } + + err = valueUpdates.Iterate(func(fieldName string, value interface{}) bool { + fieldIndex, ok := o.valueFieldIndices[fieldName] + if !ok { + panic(fmt.Sprintf("field %q not found in object type %q", fieldName, o.objectType.Name)) + } + + values[fieldIndex] = value + return true + }) + if err != nil { + return err + } + + update.Value = values + } + + o.objects.Set(keyStr, update) + } + + return nil +} + +func (o *ObjectCollection) UpdateGen() *rapid.Generator[schema.ObjectUpdate] { + return o.updateGen +} + +func (o *ObjectCollection) ScanState(f func(schema.ObjectUpdate) error) error { + var err error + o.objects.Scan(func(_ string, v schema.ObjectUpdate) bool { + err = f(v) + return err == nil + }) + return err +} + +func (o *ObjectCollection) GetObject(key any) (schema.ObjectUpdate, bool) { + return o.objects.Get(fmt.Sprintf("%v", key)) +} + +func (o *ObjectCollection) ObjectType() schema.ObjectType { + return o.objectType +} + +func (o *ObjectCollection) Len() int { + return o.objects.Len() +} diff --git a/schema/testing/statesim/options.go b/schema/testing/statesim/options.go new file mode 100644 index 000000000000..946138ea7214 --- /dev/null +++ b/schema/testing/statesim/options.go @@ -0,0 +1,5 @@ +package statesim + +type Options struct { + CanRetainDeletions bool +} diff --git a/schema/unique.go b/schema/unique.go new file mode 100644 index 000000000000..9d6db7d88700 --- /dev/null +++ b/schema/unique.go @@ -0,0 +1,5 @@ +package schema + +type UniqueConstraint struct { + FieldNames []string +} diff --git a/simapp/app_di.go b/simapp/app_di.go index ac73c5af53fb..03deafc3b3de 100644 --- a/simapp/app_di.go +++ b/simapp/app_di.go @@ -12,6 +12,7 @@ import ( "github.com/spf13/cast" clienthelpers "cosmossdk.io/client/v2/helpers" + "cosmossdk.io/core/appmodule" "cosmossdk.io/core/legacy" "cosmossdk.io/depinject" "cosmossdk.io/log" @@ -53,6 +54,10 @@ import ( servertypes "github.com/cosmos/cosmos-sdk/server/types" testdata_pulsar "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb" "github.com/cosmos/cosmos-sdk/types/module" + + _ "github.com/jackc/pgx/v5/stdlib" + + _ "cosmossdk.io/indexer/postgres" ) // DefaultNodeHome default home directories for the application daemon @@ -124,6 +129,9 @@ func NewSimApp( appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), ) *SimApp { + data := appOpts.Get("indexer").(map[string]interface{})["postgres"] + logger.Info("AppOptions", "data", data) + var ( app = &SimApp{} appBuilder *runtime.AppBuilder @@ -179,8 +187,10 @@ func NewSimApp( ) ) + var appModules map[string]appmodule.AppModule if err := depinject.Inject(appConfig, &appBuilder, + &appModules, &app.appCodec, &app.legacyAmino, &app.txConfig, @@ -242,9 +252,20 @@ func NewSimApp( app.App = appBuilder.Build(db, traceStore, baseAppOptions...) - // register streaming services - if err := app.RegisterStreamingServices(appOpts, app.kvStoreKeys()); err != nil { - panic(err) + if indexerOpts := appOpts.Get("indexer"); indexerOpts != nil { + moduleSet := map[string]any{} + for modName, mod := range appModules { + moduleSet[modName] = mod + } + err := app.EnableIndexer(indexerOpts, app.kvStoreKeys(), moduleSet) + if err != nil { + panic(err) + } + } else { + // register streaming services + if err := app.RegisterStreamingServices(appOpts, app.kvStoreKeys()); err != nil { + panic(err) + } } /**** Module Options ****/ diff --git a/simapp/go.mod b/simapp/go.mod index 7acfc530b54c..d09a44f131fd 100644 --- a/simapp/go.mod +++ b/simapp/go.mod @@ -47,7 +47,11 @@ require ( require github.com/cometbft/cometbft/api v1.0.0-rc.1 -require cosmossdk.io/x/consensus v0.0.0-00010101000000-000000000000 +require ( + cosmossdk.io/indexer/postgres v0.0.0-00010101000000-000000000000 + cosmossdk.io/x/consensus v0.0.0-00010101000000-000000000000 + github.com/jackc/pgx/v5 v5.6.0 +) require ( buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect @@ -60,6 +64,7 @@ require ( cloud.google.com/go/storage v1.42.0 // indirect cosmossdk.io/core/testing v0.0.0-00010101000000-000000000000 // indirect cosmossdk.io/errors v1.0.1 // indirect + cosmossdk.io/schema v0.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect @@ -147,6 +152,9 @@ require ( github.com/huandu/skiplist v1.2.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -245,7 +253,9 @@ replace ( cosmossdk.io/core => ../core cosmossdk.io/core/testing => ../core/testing cosmossdk.io/depinject => ../depinject + cosmossdk.io/indexer/postgres => ../indexer/postgres cosmossdk.io/log => ../log + cosmossdk.io/schema => ../schema cosmossdk.io/store => ../store cosmossdk.io/tools/confix => ../tools/confix cosmossdk.io/x/accounts => ../x/accounts diff --git a/simapp/go.sum b/simapp/go.sum index 69973a085acc..ee740f82d77a 100644 --- a/simapp/go.sum +++ b/simapp/go.sum @@ -594,6 +594,14 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= diff --git a/types/collections.go b/types/collections.go index cf7ad0cb2257..1ea7aee947e1 100644 --- a/types/collections.go +++ b/types/collections.go @@ -5,9 +5,10 @@ import ( "fmt" "time" + "cosmossdk.io/math" + "cosmossdk.io/collections" collcodec "cosmossdk.io/collections/codec" - "cosmossdk.io/math" ) var ( diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go index 48a725989b3b..b0d7a8251a1e 100644 --- a/x/bank/keeper/view.go +++ b/x/bank/keeper/view.go @@ -9,6 +9,7 @@ import ( "cosmossdk.io/core/appmodule" errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" + "cosmossdk.io/x/bank/types" "github.com/cosmos/cosmos-sdk/codec" @@ -75,10 +76,10 @@ func NewBaseViewKeeper(env appmodule.Environment, cdc codec.BinaryCodec, ak type Environment: env, cdc: cdc, ak: ak, - Supply: collections.NewMap(sb, types.SupplyKey, "supply", collections.StringKey, sdk.IntValue), + Supply: collections.NewMap(sb, types.SupplyKey, "supply", collections.StringKey.WithName("denom"), sdk.IntValue), DenomMetadata: collections.NewMap(sb, types.DenomMetadataPrefix, "denom_metadata", collections.StringKey, codec.CollValue[types.Metadata](cdc)), SendEnabled: collections.NewMap(sb, types.SendEnabledPrefix, "send_enabled", collections.StringKey, codec.BoolValue), // NOTE: we use a bool value which uses protobuf to retain state backwards compat - Balances: collections.NewIndexedMap(sb, types.BalancesPrefix, "balances", collections.PairKeyCodec(sdk.AccAddressKey, collections.StringKey), types.BalanceValueCodec, newBalancesIndexes(sb)), + Balances: collections.NewIndexedMap(sb, types.BalancesPrefix, "balances", collections.NamedPairKeyCodec("account", sdk.AccAddressKey, "denom", collections.StringKey), types.BalanceValueCodec, newBalancesIndexes(sb)), Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), } diff --git a/x/bank/module.go b/x/bank/module.go index 78295ca268e6..04ee204e26ad 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" + "cosmossdk.io/collections" + "cosmossdk.io/schema" gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" "google.golang.org/grpc" @@ -12,6 +14,7 @@ import ( "cosmossdk.io/core/appmodule" "cosmossdk.io/core/legacy" "cosmossdk.io/core/registry" + "cosmossdk.io/x/bank/client/cli" "cosmossdk.io/x/bank/keeper" "cosmossdk.io/x/bank/simulation" @@ -39,6 +42,8 @@ var ( _ appmodule.HasMigrations = AppModule{} _ appmodule.HasGenesis = AppModule{} _ appmodule.HasRegisterInterfaces = AppModule{} + + _ schema.HasModuleCodec = AppModule{} ) // AppModule implements an application module for the bank module. @@ -178,3 +183,7 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp simState.AppParams, simState.Cdc, simState.TxConfig, am.accountKeeper, am.keeper, ) } + +func (am AppModule) ModuleCodec() (schema.ModuleCodec, error) { + return am.keeper.(keeper.BaseKeeper).Schema.ModuleCodec(collections.IndexingOptions{}) +} diff --git a/x/distribution/go.mod b/x/distribution/go.mod index 200b88b129d8..b27add8ee56b 100644 --- a/x/distribution/go.mod +++ b/x/distribution/go.mod @@ -183,6 +183,7 @@ replace ( cosmossdk.io/core/testing => ../../core/testing cosmossdk.io/depinject => ../../depinject cosmossdk.io/log => ../../log + cosmossdk.io/schema => ../../schema cosmossdk.io/x/accounts => ../accounts cosmossdk.io/x/auth => ../auth cosmossdk.io/x/bank => ../bank diff --git a/x/distribution/module.go b/x/distribution/module.go index 12db17e3089a..b143df965f9d 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -9,9 +9,13 @@ import ( "github.com/spf13/cobra" "google.golang.org/grpc" + "cosmossdk.io/collections" + "cosmossdk.io/schema" + "cosmossdk.io/core/appmodule" "cosmossdk.io/core/legacy" "cosmossdk.io/core/registry" + "cosmossdk.io/x/distribution/client/cli" "cosmossdk.io/x/distribution/keeper" "cosmossdk.io/x/distribution/simulation" @@ -194,3 +198,7 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp am.accountKeeper, am.bankKeeper, am.keeper, am.stakingKeeper, ) } + +func (am AppModule) ModuleDecoder() (schema.ModuleCodec, error) { + return am.keeper.Schema.ModuleCodec(collections.IndexingOptions{}) +} diff --git a/x/staking/module.go b/x/staking/module.go index f8080c182992..873bac558d24 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -13,6 +13,7 @@ import ( "cosmossdk.io/core/legacy" "cosmossdk.io/core/registry" "cosmossdk.io/depinject" + "cosmossdk.io/x/staking/client/cli" "cosmossdk.io/x/staking/keeper" "cosmossdk.io/x/staking/types" @@ -174,3 +175,7 @@ func (AppModule) ConsensusVersion() uint64 { return consensusVersion } func (am AppModule) EndBlock(ctx context.Context) ([]appmodule.ValidatorUpdate, error) { return am.keeper.EndBlocker(ctx) } + +//func (am AppModule) ModuleCodec() (indexerbase.ModuleCodec, error) { +// return am.keeper.Schema.ModuleCodec(collections.IndexingOptions{}) +//}