├── .gitignore ├── .gitmodules ├── README.md ├── web-rtc ├── README.md └── main.go ├── env-config ├── README.md └── main.go ├── vector-search ├── README.md ├── google_ai.go └── main.go ├── full-text-search ├── README.md └── main.go ├── main.go ├── go.mod ├── LICENSE └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | pb_data/ 2 | pb_migrations/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/pocketbase"] 2 | path = third_party/pocketbase 3 | url = https://github.com/rodydavis/pocketbase 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PocketBase Plugins 2 | 3 | Running the example: 4 | 5 | ```bash 6 | go run --tags "fts5" . serve 7 | ``` 8 | 9 | ## Plugins 10 | 11 | - [Full Text Search](/full-text-search/README.md) 12 | - [Vector Search/Store](/vector-search/README.md) 13 | - [WebRTC](/web-rtc/README.md) 14 | - [ENV Config](/env-config/README.md) 15 | -------------------------------------------------------------------------------- /web-rtc/README.md: -------------------------------------------------------------------------------- 1 | # WebRTC 2 | 3 | ## Getting Started 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "log" 10 | "github.com/pocketbase/pocketbase" 11 | web_rtc "github.com/rodydavis/pocketbase-plugins/web-rtc" 12 | ) 13 | 14 | func main() { 15 | app := pocketbase.New() 16 | 17 | err = web_rtc.Init(app) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | if err := app.Start(); err != nil { 23 | log.Fatal(err) 24 | } 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /env-config/README.md: -------------------------------------------------------------------------------- 1 | # ENV Config 2 | 3 | ## Getting Started 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "log" 10 | "github.com/pocketbase/pocketbase" 11 | env_config "github.com/rodydavis/pocketbase-plugins/env-config" 12 | ) 13 | 14 | func main() { 15 | app := pocketbase.New() 16 | 17 | err = env_config.Init(app) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | if err := app.Start(); err != nil { 23 | log.Fatal(err) 24 | } 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /vector-search/README.md: -------------------------------------------------------------------------------- 1 | # Vector Store 2 | 3 | ## Getting Started 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "log" 10 | "github.com/pocketbase/pocketbase" 11 | vector_store "github.com/rodydavis/pocketbase-plugins/vector-store" 12 | ) 13 | 14 | func main() { 15 | app := pocketbase.New() 16 | 17 | err = vector_store.Init(app, "vectors") 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | if err := app.Start(); err != nil { 23 | log.Fatal(err) 24 | } 25 | } 26 | ``` 27 | 28 | ### REST API 29 | 30 | ```curl 31 | curl -X GET http://127.0.0.1:8090/api/collections/vectors/records/vector-search?search=Hello 32 | ``` 33 | -------------------------------------------------------------------------------- /full-text-search/README.md: -------------------------------------------------------------------------------- 1 | # Full Text Search 2 | 3 | ## Getting Started 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "log" 10 | "github.com/pocketbase/pocketbase" 11 | full_text_search "github.com/rodydavis/pocketbase-plugins/full-text-search" 12 | ) 13 | 14 | func main() { 15 | app := pocketbase.New() 16 | 17 | err = full_text_search.Init(app, "posts", "comments") 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | if err := app.Start(); err != nil { 23 | log.Fatal(err) 24 | } 25 | } 26 | ``` 27 | 28 | ### REST API 29 | 30 | ```curl 31 | curl -X GET http://127.0.0.1:8090/api/collections/posts/records/full-text-search?search=Hello 32 | ``` 33 | -------------------------------------------------------------------------------- /vector-search/google_ai.go: -------------------------------------------------------------------------------- 1 | package vector_search 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/google/generative-ai-go/genai" 8 | "google.golang.org/api/option" 9 | ) 10 | 11 | func createGoogleAiClient() (*genai.Client, error) { 12 | var apiKey string = os.Getenv("GOOGLE_AI_API_KEY") 13 | ctx := context.Background() 14 | client, err := genai.NewClient(ctx, option.WithAPIKey(apiKey)) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return client, nil 19 | } 20 | 21 | func googleAiEmbedContent(client *genai.Client, taskType genai.TaskType, title string, parts ...genai.Part) ([]float32, error) { 22 | ctx := context.Background() 23 | model := googleAiEmbeddingModel(client) 24 | model.TaskType = taskType 25 | res, err := model.EmbedContentWithTitle(ctx, title, parts...) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return res.Embedding.Values, nil 30 | } 31 | 32 | func googleAiEmbeddingModel(client *genai.Client) *genai.EmbeddingModel { 33 | em := client.EmbeddingModel("text-embedding-004") 34 | return em 35 | } 36 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/pocketbase/pocketbase" 7 | env_config "github.com/rodydavis/pocketbase-plugins/env-config" 8 | full_text_search "github.com/rodydavis/pocketbase-plugins/full-text-search" 9 | vector_search "github.com/rodydavis/pocketbase-plugins/vector-search" 10 | web_rtc "github.com/rodydavis/pocketbase-plugins/web-rtc" 11 | ) 12 | 13 | func main() { 14 | app := pocketbase.New() 15 | 16 | err := env_config.Init(app) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | err = web_rtc.Init(app) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | vectorCollections := []vector_search.VectorCollection{ 27 | { 28 | Name: "vectors", 29 | }, 30 | } 31 | err = vector_search.Init(app, vectorCollections...) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | 36 | fullTextSearchCollections := []string{} 37 | for _, col := range vectorCollections { 38 | fullTextSearchCollections = append(fullTextSearchCollections, col.Name) 39 | } 40 | err = full_text_search.Init(app, fullTextSearchCollections...) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | if err := app.Start(); err != nil { 46 | log.Fatal(err) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /env-config/main.go: -------------------------------------------------------------------------------- 1 | package env_config 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/labstack/echo/v5" 9 | "github.com/pocketbase/pocketbase" 10 | "github.com/pocketbase/pocketbase/apis" 11 | "github.com/pocketbase/pocketbase/core" 12 | "github.com/pocketbase/pocketbase/plugins/ghupdate" 13 | "github.com/pocketbase/pocketbase/plugins/jsvm" 14 | "github.com/pocketbase/pocketbase/plugins/migratecmd" 15 | ) 16 | 17 | func Init(app *pocketbase.PocketBase, collections ...string) error { 18 | 19 | var hooksDir string = os.Getenv("PB_HOOKS_DIR") 20 | var hooksWatch bool = true 21 | var hooksPool int = 25 22 | var migrationsDir string 23 | var automigrate bool = true 24 | var publicDir string = "./pb_public" 25 | var indexFallback bool = true 26 | var queryTimeout int = 30 27 | 28 | if os.Getenv("PB_HOOKS_DIR") != "" { 29 | hooksDir = os.Getenv("PB_HOOKS_DIR") 30 | } 31 | 32 | if os.Getenv("PB_HOOKS_WATCH") != "" { 33 | value, err := strconv.ParseBool(os.Getenv("PB_HOOKS_WATCH")) 34 | if err != nil { 35 | hooksWatch = value 36 | } 37 | } 38 | 39 | if os.Getenv("PB_HOOKS_POOL") != "" { 40 | value, err := strconv.Atoi(os.Getenv("PB_HOOKS_POOL")) 41 | if err == nil { 42 | hooksPool = value 43 | } 44 | } 45 | 46 | if os.Getenv("PB_AUTO_MIGRATE") != "" { 47 | value, err := strconv.ParseBool(os.Getenv("PB_AUTO_MIGRATE")) 48 | if err != nil { 49 | automigrate = value 50 | } 51 | } 52 | 53 | if os.Getenv("PB_PUBLIC_DIR") != "" { 54 | publicDir = os.Getenv("PB_PUBLIC_DIR") 55 | } 56 | 57 | if os.Getenv("PB_INDEX_FALLBACK") != "" { 58 | value, err := strconv.ParseBool(os.Getenv("PB_INDEX_FALLBACK")) 59 | if err != nil { 60 | indexFallback = value 61 | } 62 | } 63 | 64 | if os.Getenv("PB_QUERY_TIMEOUT") != "" { 65 | value, err := strconv.Atoi(os.Getenv("PB_QUERY_TIMEOUT")) 66 | if err == nil { 67 | queryTimeout = value 68 | } 69 | } 70 | 71 | jsvm.MustRegister(app, jsvm.Config{ 72 | MigrationsDir: migrationsDir, 73 | HooksDir: hooksDir, 74 | HooksWatch: hooksWatch, 75 | HooksPoolSize: hooksPool, 76 | }) 77 | 78 | migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{ 79 | TemplateLang: migratecmd.TemplateLangJS, 80 | Automigrate: automigrate, 81 | Dir: migrationsDir, 82 | }) 83 | 84 | // Owner specifies the account owner of the repository (default to "pocketbase"). 85 | Owner := "pocketbase" 86 | if os.Getenv("PB_GH_UPDATE_OWNER") != "" { 87 | Owner = os.Getenv("PB_GH_UPDATE_OWNER") 88 | } 89 | 90 | // Repo specifies the name of the repository (default to "pocketbase"). 91 | Repo := "pocketbase" 92 | if os.Getenv("PB_GH_UPDATE_REPO") != "" { 93 | Repo = os.Getenv("PB_GH_UPDATE_REPO") 94 | } 95 | 96 | // ArchiveExecutable specifies the name of the executable file in the release archive 97 | // (default to "pocketbase"; an additional ".exe" check is also performed as a fallback). 98 | ArchiveExecutable := "pocketbase" 99 | if os.Getenv("PB_GH_UPDATE_ARCHIVE_EXECUTABLE") != "" { 100 | ArchiveExecutable = os.Getenv("PB_GH_UPDATE_ARCHIVE_EXECUTABLE") 101 | } 102 | 103 | ghupdate.MustRegister(app, app.RootCmd, ghupdate.Config{ 104 | Owner: Owner, 105 | Repo: Repo, 106 | ArchiveExecutable: ArchiveExecutable, 107 | }) 108 | 109 | app.OnAfterBootstrap().PreAdd(func(e *core.BootstrapEvent) error { 110 | app.Dao().ModelQueryTimeout = time.Duration(queryTimeout) * time.Second 111 | 112 | return nil 113 | }) 114 | 115 | app.OnBeforeServe().Add(func(e *core.ServeEvent) error { 116 | e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS(publicDir), indexFallback), crossOriginHeaders) 117 | app.OnFileDownloadRequest().Add(func(e *core.FileDownloadEvent) error { 118 | e.HttpContext.Response().Header().Set("Cache-Control", "public, max-age=31536000, immutable") 119 | return nil 120 | }) 121 | return nil 122 | }) 123 | 124 | return nil 125 | } 126 | 127 | func crossOriginHeaders(next echo.HandlerFunc) echo.HandlerFunc { 128 | return func(c echo.Context) error { 129 | c.Response().Header().Set("Cross-Origin-Opener-Policy", "same-origin") 130 | c.Response().Header().Set("Cross-Origin-Embedder-Policy", "credentialless") 131 | if err := next(c); err != nil { 132 | c.Error(err) 133 | } 134 | return nil 135 | } 136 | } 137 | 138 | // func defaultCacheHeaders(next echo.HandlerFunc) echo.HandlerFunc { 139 | // return func(c echo.Context) error { 140 | // // c.Response().Header().Set("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400") 141 | // c.Response().Header().Set("Cache-Control", "public, max-age=1, stale-while-revalidate=59") 142 | // if err := next(c); err != nil { 143 | // c.Error(err) 144 | // } 145 | // return nil 146 | // } 147 | // } 148 | -------------------------------------------------------------------------------- /web-rtc/main.go: -------------------------------------------------------------------------------- 1 | package web_rtc 2 | 3 | import ( 4 | "github.com/pocketbase/pocketbase" 5 | "github.com/pocketbase/pocketbase/core" 6 | "github.com/pocketbase/pocketbase/models" 7 | "github.com/pocketbase/pocketbase/models/schema" 8 | "github.com/pocketbase/pocketbase/tools/types" 9 | ) 10 | 11 | var StunServers = []string{ 12 | "stun:stun1.l.google.com:19302", 13 | "stun:stun2.l.google.com:19302", 14 | } 15 | 16 | var ( 17 | AuthCollection = "users" 18 | IceServerCollection = "ice_servers" 19 | CallsCollection = "calls" 20 | OfferCandidatesCollection = "offer_candidates" 21 | AnswerCandidatesCollection = "answer_candidates" 22 | ) 23 | 24 | func Init(app *pocketbase.PocketBase) error { 25 | app.OnBeforeServe().Add(func(e *core.ServeEvent) error { 26 | iceServer, err := createIceServerCollection(app, IceServerCollection) 27 | if err != nil { 28 | return err 29 | } 30 | if iceServer != nil { 31 | for _, server := range StunServers { 32 | record := models.NewRecord(iceServer) 33 | record.Set("url", server) 34 | if err := app.Dao().SaveRecord(record); err != nil { 35 | return err 36 | } 37 | } 38 | } 39 | _, err = createCallsServerCollection(app, CallsCollection, AuthCollection) 40 | if err != nil { 41 | return err 42 | } 43 | _, err = createCandidatesServerCollection(app, OfferCandidatesCollection, CallsCollection) 44 | if err != nil { 45 | return err 46 | } 47 | _, err = createCandidatesServerCollection(app, AnswerCandidatesCollection, CallsCollection) 48 | if err != nil { 49 | return err 50 | } 51 | return nil 52 | }) 53 | 54 | return nil 55 | } 56 | 57 | func createIceServerCollection(app *pocketbase.PocketBase, target string) (*models.Collection, error) { 58 | current, _ := app.Dao().FindCollectionByNameOrId(target) 59 | if current != nil { 60 | return nil, nil 61 | } 62 | fields := []*schema.SchemaField{ 63 | { 64 | Name: "url", 65 | Type: schema.FieldTypeText, 66 | Required: true, 67 | }, 68 | } 69 | collection := &models.Collection{ 70 | Name: target, 71 | Type: models.CollectionTypeBase, 72 | Schema: schema.NewSchema(fields...), 73 | ListRule: types.Pointer("@request.auth.id != ''"), 74 | ViewRule: types.Pointer("@request.auth.id != ''"), 75 | Indexes: types.JsonArray[string]{ 76 | "CREATE UNIQUE INDEX idx_" + target + " ON " + target + " (url);", 77 | }, 78 | } 79 | 80 | if err := app.Dao().SaveCollection(collection); err != nil { 81 | return nil, err 82 | } 83 | 84 | return collection, nil 85 | } 86 | 87 | func createCallsServerCollection(app *pocketbase.PocketBase, target string, authCollection string) (*models.Collection, error) { 88 | current, _ := app.Dao().FindCollectionByNameOrId(target) 89 | if current != nil { 90 | return nil, nil 91 | } 92 | auth, err := app.Dao().FindCollectionByNameOrId(authCollection) 93 | if err != nil { 94 | return nil, err 95 | } 96 | fields := []*schema.SchemaField{ 97 | { 98 | Name: "user_id", 99 | Type: schema.FieldTypeRelation, 100 | Required: true, 101 | Options: &schema.RelationOptions{ 102 | MaxSelect: types.Pointer(1), 103 | CollectionId: auth.Id, 104 | CascadeDelete: true, 105 | }, 106 | }, 107 | { 108 | Name: "offer", 109 | Type: schema.FieldTypeJson, 110 | }, 111 | { 112 | Name: "answer", 113 | Type: schema.FieldTypeJson, 114 | }, 115 | } 116 | collection := &models.Collection{ 117 | Name: target, 118 | Type: models.CollectionTypeBase, 119 | Schema: schema.NewSchema(fields...), 120 | ListRule: types.Pointer("@request.auth.id != ''"), 121 | ViewRule: types.Pointer("@request.auth.id != ''"), 122 | CreateRule: types.Pointer("@request.auth.id != ''"), 123 | UpdateRule: types.Pointer("@request.auth.id != ''"), 124 | DeleteRule: types.Pointer("@request.auth.id != ''"), 125 | Indexes: types.JsonArray[string]{ 126 | "CREATE UNIQUE INDEX idx_" + target + " ON " + target + " (user_id);", 127 | }, 128 | } 129 | 130 | if err := app.Dao().SaveCollection(collection); err != nil { 131 | return nil, err 132 | } 133 | 134 | return collection, nil 135 | } 136 | 137 | func createCandidatesServerCollection(app *pocketbase.PocketBase, target string, callsCollection string) (*models.Collection, error) { 138 | current, _ := app.Dao().FindCollectionByNameOrId(target) 139 | if current != nil { 140 | return nil, nil 141 | } 142 | calls, err := app.Dao().FindCollectionByNameOrId(callsCollection) 143 | if err != nil { 144 | return nil, err 145 | } 146 | fields := []*schema.SchemaField{ 147 | { 148 | Name: "call_id", 149 | Type: schema.FieldTypeRelation, 150 | Required: true, 151 | Options: &schema.RelationOptions{ 152 | MaxSelect: types.Pointer(1), 153 | CollectionId: calls.Id, 154 | CascadeDelete: true, 155 | }, 156 | }, 157 | { 158 | Name: "data", 159 | Type: schema.FieldTypeJson, 160 | }, 161 | } 162 | collection := &models.Collection{ 163 | Name: target, 164 | Type: models.CollectionTypeBase, 165 | Schema: schema.NewSchema(fields...), 166 | ListRule: types.Pointer("@request.auth.id != ''"), 167 | ViewRule: types.Pointer("@request.auth.id != ''"), 168 | CreateRule: types.Pointer("@request.auth.id != ''"), 169 | UpdateRule: types.Pointer("@request.auth.id != ''"), 170 | DeleteRule: types.Pointer("@request.auth.id != ''"), 171 | Indexes: types.JsonArray[string]{ 172 | "CREATE UNIQUE INDEX idx_" + target + " ON " + target + " (call_id);", 173 | }, 174 | } 175 | 176 | if err := app.Dao().SaveCollection(collection); err != nil { 177 | return nil, err 178 | } 179 | 180 | return collection, nil 181 | } 182 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rodydavis/pocketbase-plugins 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/google/generative-ai-go v0.17.0 7 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 8 | github.com/pocketbase/pocketbase v0.22.19 9 | ) 10 | 11 | require ( 12 | cloud.google.com/go v0.115.0 // indirect 13 | cloud.google.com/go/ai v0.8.0 // indirect 14 | cloud.google.com/go/auth v0.7.2 // indirect 15 | cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect 16 | cloud.google.com/go/compute/metadata v0.5.0 // indirect 17 | cloud.google.com/go/longrunning v0.5.7 // indirect 18 | github.com/felixge/httpsnoop v1.0.4 // indirect 19 | github.com/go-logr/logr v1.4.2 // indirect 20 | github.com/go-logr/stdr v1.2.2 // indirect 21 | github.com/golang/protobuf v1.5.4 // indirect 22 | github.com/google/s2a-go v0.1.7 // indirect 23 | github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect 24 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect 25 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect 26 | go.opentelemetry.io/otel v1.26.0 // indirect 27 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 28 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 29 | google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect 30 | ) 31 | 32 | replace github.com/pocketbase/pocketbase => ./third_party/pocketbase 33 | 34 | require ( 35 | github.com/AlecAivazis/survey/v2 v2.3.7 // indirect 36 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 37 | github.com/asg017/sqlite-vec-go-bindings v0.1.1 38 | github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect 39 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect 40 | github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect 41 | github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect 42 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect 43 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 // indirect 44 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect 45 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect 46 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect 47 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect 48 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect 49 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect 50 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect 51 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect 52 | github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 // indirect 53 | github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect 54 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect 55 | github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect 56 | github.com/aws/smithy-go v1.20.3 // indirect 57 | github.com/disintegration/imaging v1.6.2 // indirect 58 | github.com/dlclark/regexp2 v1.11.0 // indirect 59 | github.com/domodwyer/mailyak/v3 v3.6.2 // indirect 60 | github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2 // indirect 61 | github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf // indirect 62 | github.com/dustin/go-humanize v1.0.1 // indirect 63 | github.com/fatih/color v1.17.0 // indirect 64 | github.com/fsnotify/fsnotify v1.7.0 // indirect 65 | github.com/gabriel-vasile/mimetype v1.4.4 // indirect 66 | github.com/ganigeorgiev/fexpr v0.4.1 // indirect 67 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect 68 | github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect 69 | github.com/goccy/go-json v0.10.3 // indirect 70 | github.com/golang-jwt/jwt/v4 v4.5.0 // indirect 71 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 72 | github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0 // indirect 73 | github.com/google/uuid v1.6.0 // indirect 74 | github.com/googleapis/gax-go/v2 v2.13.0 // indirect 75 | github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 76 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 77 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 78 | github.com/mattn/go-colorable v0.1.13 // indirect 79 | github.com/mattn/go-isatty v0.0.20 // indirect 80 | github.com/mattn/go-sqlite3 v1.14.22 // indirect 81 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 82 | github.com/ncruces/go-strftime v0.1.9 // indirect 83 | github.com/pocketbase/dbx v1.10.1 84 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 85 | github.com/spf13/cast v1.6.0 // indirect 86 | github.com/spf13/cobra v1.8.1 // indirect 87 | github.com/spf13/pflag v1.0.5 // indirect 88 | github.com/valyala/bytebufferpool v1.0.0 // indirect 89 | github.com/valyala/fasttemplate v1.2.2 // indirect 90 | go.opencensus.io v0.24.0 // indirect 91 | gocloud.dev v0.37.0 // indirect 92 | golang.org/x/crypto v0.25.0 // indirect 93 | golang.org/x/image v0.18.0 // indirect 94 | golang.org/x/net v0.27.0 // indirect 95 | golang.org/x/oauth2 v0.21.0 // indirect 96 | golang.org/x/sync v0.7.0 // indirect 97 | golang.org/x/sys v0.22.0 // indirect 98 | golang.org/x/term v0.22.0 // indirect 99 | golang.org/x/text v0.16.0 // indirect 100 | golang.org/x/time v0.5.0 // indirect 101 | golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect 102 | google.golang.org/api v0.189.0 103 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a // indirect 104 | google.golang.org/grpc v1.65.0 // indirect 105 | google.golang.org/protobuf v1.34.2 // indirect 106 | modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e // indirect 107 | modernc.org/libc v1.55.3 // indirect 108 | modernc.org/mathutil v1.6.0 // indirect 109 | modernc.org/memory v1.8.0 // indirect 110 | modernc.org/sqlite v1.31.1 // indirect 111 | modernc.org/strutil v1.2.0 // indirect 112 | modernc.org/token v1.1.0 // indirect 113 | ) 114 | -------------------------------------------------------------------------------- /full-text-search/main.go: -------------------------------------------------------------------------------- 1 | package full_text_search 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/labstack/echo/v5" 8 | "github.com/pocketbase/dbx" 9 | "github.com/pocketbase/pocketbase" 10 | "github.com/pocketbase/pocketbase/apis" 11 | "github.com/pocketbase/pocketbase/core" 12 | "github.com/pocketbase/pocketbase/models" 13 | ) 14 | 15 | // https://www.sqlite.org/fts5.html#external_content_tables 16 | func Init(app *pocketbase.PocketBase, collections ...string) error { 17 | app.OnCollectionAfterCreateRequest().Add(func(e *core.CollectionCreateEvent) error { 18 | target := e.Collection.Name 19 | for _, col := range collections { 20 | if col == target { 21 | err := createCollectionFts(app, target) 22 | if err != nil { 23 | app.Logger().Error(fmt.Sprint(err)) 24 | return err 25 | } 26 | } 27 | } 28 | return nil 29 | }) 30 | app.OnCollectionAfterUpdateRequest().Add(func(e *core.CollectionUpdateEvent) error { 31 | target := e.Collection.Name 32 | for _, col := range collections { 33 | if col == target { 34 | err := deleteCollection(app, target) 35 | if err != nil { 36 | app.Logger().Error(fmt.Sprint(err)) 37 | return err 38 | } 39 | err = createCollectionFts(app, target) 40 | if err != nil { 41 | app.Logger().Error(fmt.Sprint(err)) 42 | return err 43 | } 44 | } 45 | } 46 | return nil 47 | }) 48 | app.OnCollectionAfterDeleteRequest().PreAdd(func(e *core.CollectionDeleteEvent) error { 49 | target := e.Collection.Name 50 | for _, col := range collections { 51 | if col == target { 52 | err := deleteCollection(app, target) 53 | if err != nil { 54 | app.Logger().Error(fmt.Sprint(err)) 55 | return err 56 | } 57 | } 58 | } 59 | return nil 60 | }) 61 | app.OnBeforeServe().Add(func(e *core.ServeEvent) error { 62 | for _, target := range collections { 63 | err := createCollectionFts(app, target) 64 | if err != nil { 65 | app.Logger().Error(fmt.Sprint(err)) 66 | return err 67 | } 68 | 69 | } 70 | return nil 71 | }) 72 | app.OnBeforeServe().Add(func(e *core.ServeEvent) error { 73 | group := e.Router.Group("/api/collections/:collectionIdOrName/records", apis.ActivityLogger(app)) 74 | group.GET("/full-text-search", func(c echo.Context) error { 75 | target := c.PathParam("collectionIdOrName") 76 | if _, err := app.Dao().FindCollectionByNameOrId(target); err != nil { 77 | app.Logger().Error(fmt.Sprint(err)) 78 | return err 79 | } 80 | tbl := target // collection.TableName() 81 | q := c.QueryParam("search") 82 | if q == "" { 83 | return c.NoContent(204) 84 | } 85 | var query strings.Builder 86 | query.WriteString("SELECT * ") // rank as '@rank' 87 | query.WriteString("FROM " + tbl + "_fts ") 88 | // query.WriteString("INNER JOIN " + tbl + " tbl ON tbl.id = " + tbl + "_fts._id ") 89 | query.WriteString("WHERE " + tbl + "_fts MATCH {:q} ") 90 | query.WriteString("ORDER BY rank;") 91 | 92 | results := []dbx.NullStringMap{} 93 | err := app.Dao().DB(). 94 | NewQuery(query.String()). 95 | Bind(dbx.Params{"q": q}). 96 | All(&results) 97 | if err != nil { 98 | app.Logger().Error(fmt.Sprint(err)) 99 | return err 100 | } 101 | app.Logger().Info(fmt.Sprint(results)) 102 | 103 | c.Response().Header().Set(echo.HeaderContentType, "application/json") 104 | items := []map[string]any{} 105 | for _, result := range results { 106 | m := make(map[string]interface{}) 107 | for key := range result { 108 | val := result[key] 109 | value, err := val.Value() 110 | if err != nil || !val.Valid { 111 | m[key] = nil 112 | } else { 113 | m[key] = value 114 | } 115 | } 116 | items = append(items, m) 117 | } 118 | 119 | // TODO: Paging result 120 | return c.JSON(200, items) 121 | 122 | }) 123 | return nil 124 | }) 125 | 126 | return nil 127 | } 128 | 129 | func createCollectionFts(app *pocketbase.PocketBase, target string) error { 130 | collection, err := app.Dao().FindCollectionByNameOrId(target) 131 | if err != nil { 132 | app.Logger().Error(fmt.Sprint(err)) 133 | return err 134 | } 135 | fields := collectionFields(collection, "id") 136 | exists, _ := checkIfTableExists(app, target+"_fts") 137 | 138 | if !exists { 139 | tbl := "`" + target + "`" 140 | var stmt strings.Builder 141 | stmt.WriteString("CREATE VIRTUAL TABLE " + target + "_fts USING FTS5 (") 142 | stmt.WriteString(" " + strings.Join(fields, ", ") + ",") 143 | stmt.WriteString(" content=" + target + ",") 144 | // stmt.WriteString(" content=''") 145 | // stmt.WriteString(" content_rowid='id'") 146 | stmt.WriteString(");") 147 | app.Logger().Info(stmt.String()) 148 | if _, err := app.Dao().DB().NewQuery(stmt.String()).Execute(); err != nil { 149 | app.Logger().Error(fmt.Sprint(err)) 150 | return err 151 | } 152 | 153 | stmt.Reset() 154 | stmt.WriteString("CREATE TRIGGER " + target + "_fts_insert AFTER INSERT ON " + tbl + " BEGIN ") 155 | stmt.WriteString(" INSERT INTO " + target + "_fts(" + strings.Join(fields, ", ") + ")") 156 | stmt.WriteString(" VALUES (" + strings.Join(surround(fields, "new.", ""), ", ") + ");") 157 | stmt.WriteString("END;") 158 | app.Logger().Info(stmt.String()) 159 | if _, err := app.Dao().DB().NewQuery(stmt.String()).Execute(); err != nil { 160 | app.Logger().Error(fmt.Sprint(err)) 161 | return err 162 | } 163 | 164 | stmt.Reset() 165 | stmt.WriteString("CREATE TRIGGER " + target + "_fts_update AFTER UPDATE ON " + tbl + " BEGIN ") 166 | stmt.WriteString(" INSERT INTO " + target + "_fts(" + target + "_fts, " + strings.Join(fields, ", ") + ")") 167 | stmt.WriteString(" VALUES ('delete', " + strings.Join(surround(fields, "old.", ""), ", ") + ");") 168 | stmt.WriteString(" INSERT INTO " + target + "_fts(" + strings.Join(fields, ", ") + ")") 169 | stmt.WriteString(" VALUES (" + strings.Join(surround(fields, "new.", ""), ", ") + ");") 170 | stmt.WriteString("END;") 171 | app.Logger().Info(stmt.String()) 172 | if _, err := app.Dao().DB().NewQuery(stmt.String()).Execute(); err != nil { 173 | app.Logger().Error(fmt.Sprint(err)) 174 | return err 175 | } 176 | 177 | stmt.Reset() 178 | stmt.WriteString("CREATE TRIGGER " + target + "_fts_delete AFTER DELETE ON " + tbl + " BEGIN ") 179 | stmt.WriteString(" INSERT INTO " + target + "_fts(" + target + "_fts, " + strings.Join(fields, ", ") + ")") 180 | stmt.WriteString(" VALUES ('delete', " + strings.Join(surround(fields, "old.", ""), ", ") + ");") 181 | stmt.WriteString("END;") 182 | app.Logger().Info(stmt.String()) 183 | if _, err := app.Dao().DB().NewQuery(stmt.String()).Execute(); err != nil { 184 | app.Logger().Error(fmt.Sprint(err)) 185 | return err 186 | } 187 | } 188 | 189 | err = syncCollection(app, target) 190 | if err != nil { 191 | app.Logger().Error(fmt.Sprint(err)) 192 | return err 193 | } 194 | 195 | return nil 196 | } 197 | 198 | func deleteCollection(app *pocketbase.PocketBase, target string) error { 199 | if _, err := app.Dao().DB(). 200 | NewQuery("DELETE FROM " + target + "_fts;"). 201 | Execute(); err != nil { 202 | return err 203 | } 204 | if _, err := app.Dao().DB(). 205 | NewQuery("DROP TABLE IF EXISTS " + target + "_fts;"). 206 | Execute(); err != nil { 207 | return err 208 | } 209 | return nil 210 | } 211 | 212 | func checkIfTableExists(app *pocketbase.PocketBase, target string) (bool, error) { 213 | type Meta struct { 214 | Name string `db:"name" json:"name"` 215 | } 216 | 217 | meta := &Meta{} 218 | 219 | var stmt strings.Builder 220 | stmt.WriteString("SELECT name FROM sqlite_master ") 221 | stmt.WriteString("WHERE type='table' ") 222 | stmt.WriteString("AND name = {:table_name};") 223 | 224 | app.Logger().Info(stmt.String()) 225 | if err := app.Dao().DB().NewQuery(stmt.String()).Bind(dbx.Params{"table_name": target}).One(&meta); err != nil { 226 | app.Logger().Error(fmt.Sprint(err)) 227 | return false, err 228 | } 229 | 230 | valid := meta != nil 231 | return valid, nil 232 | } 233 | 234 | func syncCollection(app *pocketbase.PocketBase, target string) error { 235 | var stmt strings.Builder 236 | stmt.WriteString("INSERT INTO " + target + "_fts(" + target + "_fts) VALUES('rebuild');") 237 | // stmt.WriteString("INSERT INTO " + target + "_fts SELECT " + strings.Join(fields, ", ") + " FROM " + target) 238 | app.Logger().Info(stmt.String()) 239 | if _, err := app.Dao().DB().NewQuery(stmt.String()).Execute(); err != nil { 240 | app.Logger().Error(fmt.Sprint(err)) 241 | return err 242 | } 243 | 244 | return nil 245 | } 246 | 247 | func collectionFields(collection *models.Collection, id string) []string { 248 | fields := []string{id} 249 | for _, field := range collection.Schema.Fields() { 250 | name := field.Name 251 | fields = append(fields, name) 252 | } 253 | return fields 254 | } 255 | 256 | func surround(items []string, prefix string, suffix string) []string { 257 | results := []string{} 258 | for i := 0; i < len(items); i++ { 259 | item := items[i] 260 | results = append(results, prefix+item+suffix) 261 | } 262 | return results 263 | } 264 | -------------------------------------------------------------------------------- /vector-search/main.go: -------------------------------------------------------------------------------- 1 | package vector_search 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | sqlite_vec "github.com/asg017/sqlite-vec-go-bindings/cgo" 10 | "github.com/google/generative-ai-go/genai" 11 | "github.com/labstack/echo/v5" 12 | "github.com/pocketbase/dbx" 13 | "github.com/pocketbase/pocketbase" 14 | "github.com/pocketbase/pocketbase/apis" 15 | "github.com/pocketbase/pocketbase/core" 16 | "github.com/pocketbase/pocketbase/models" 17 | "github.com/pocketbase/pocketbase/models/schema" 18 | "github.com/pocketbase/pocketbase/tools/types" 19 | ) 20 | 21 | type VectorCollection struct { 22 | Name string 23 | ExtraFields []*schema.SchemaField 24 | } 25 | 26 | var ColPrefix = "$$$" 27 | 28 | func Init(app *pocketbase.PocketBase, collections ...VectorCollection) error { 29 | sqlite_vec.Auto() 30 | client, err := createGoogleAiClient() 31 | if err != nil { 32 | return err 33 | } 34 | app.OnBeforeServe().Add(func(e *core.ServeEvent) error { 35 | for _, target := range collections { 36 | collection, _ := app.Dao().FindCollectionByNameOrId(target.Name) 37 | if collection == nil { 38 | err := createCollection(app, target.Name, target.ExtraFields...) 39 | if err != nil { 40 | app.Logger().Error(fmt.Sprint(err)) 41 | return err 42 | } 43 | } 44 | } 45 | return nil 46 | }) 47 | app.OnModelAfterCreate().Add(func(e *core.ModelEvent) error { 48 | tbl := e.Model.TableName() 49 | for _, target := range collections { 50 | if tbl == target.Name { 51 | err := modelModify(app, target.Name, client, e) 52 | if err != nil { 53 | app.Logger().Error(fmt.Sprint(err)) 54 | return err 55 | } 56 | } 57 | } 58 | return nil 59 | }) 60 | app.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error { 61 | tbl := e.Model.TableName() 62 | for _, target := range collections { 63 | if tbl == target.Name { 64 | err := modelModify(app, target.Name, client, e) 65 | if err != nil { 66 | app.Logger().Error(fmt.Sprint(err)) 67 | return err 68 | } 69 | } 70 | } 71 | return nil 72 | }) 73 | app.OnModelAfterDelete().Add(func(e *core.ModelEvent) error { 74 | tbl := e.Model.TableName() 75 | for _, target := range collections { 76 | if tbl == target.Name { 77 | err := modelDelete(app, target.Name, e) 78 | if err != nil { 79 | app.Logger().Error(fmt.Sprint(err)) 80 | return err 81 | } 82 | } 83 | } 84 | return nil 85 | }) 86 | app.OnCollectionAfterDeleteRequest().Add(func(e *core.CollectionDeleteEvent) error { 87 | for _, target := range collections { 88 | if e.Collection.Name == target.Name { 89 | err := deleteCollection(app, target.Name) 90 | if err != nil { 91 | app.Logger().Error(fmt.Sprint(err)) 92 | return err 93 | } 94 | } 95 | } 96 | return nil 97 | }) 98 | app.OnBeforeServe().Add(func(e *core.ServeEvent) error { 99 | group := e.Router.Group("/api/collections/:collectionIdOrName/records", apis.ActivityLogger(app)) 100 | group.GET("/vector-search", func(c echo.Context) error { 101 | target := c.PathParam("collectionIdOrName") 102 | if _, err := app.Dao().FindCollectionByNameOrId(target); err != nil { 103 | app.Logger().Error(fmt.Sprint(err)) 104 | return err 105 | } 106 | 107 | title := c.QueryParam("title") 108 | content := c.QueryParam("search") 109 | k := c.QueryParam("k") 110 | kNum := 5 111 | if k != "" { 112 | val, err := strconv.Atoi(k) 113 | if err == nil { 114 | kNum = val 115 | } 116 | } 117 | 118 | if content == "" { 119 | return c.NoContent(204) 120 | } 121 | 122 | vector, err := googleAiEmbedContent(client, genai.TaskTypeRetrievalQuery, title, genai.Text(content)) 123 | if err != nil { 124 | return err 125 | } 126 | jsonVec, err := json.Marshal(vector) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | stmt := "SELECT v.id, distance, v.content, v.created, v.updated " 132 | stmt += "FROM " + target + "_embeddings " 133 | stmt += "LEFT JOIN " + target + " v ON v.vector_id = " + target + "_embeddings.id " 134 | stmt += "WHERE embedding MATCH {:embedding} " 135 | stmt += "AND k = {:k};" 136 | 137 | results := []dbx.NullStringMap{} 138 | err = app.Dao().DB(). 139 | NewQuery(stmt). 140 | Bind(dbx.Params{ 141 | "embedding": string(jsonVec), 142 | "k": kNum, 143 | }). 144 | All(&results) 145 | if err != nil { 146 | app.Logger().Error(fmt.Sprint(err)) 147 | return err 148 | } 149 | app.Logger().Info(fmt.Sprint(results)) 150 | 151 | c.Response().Header().Set(echo.HeaderContentType, "application/json") 152 | items := []map[string]any{} 153 | for _, result := range results { 154 | m := make(map[string]interface{}) 155 | for key := range result { 156 | val := result[key] 157 | value, err := val.Value() 158 | if err != nil || !val.Valid { 159 | m[key] = nil 160 | } else { 161 | m[key] = value 162 | } 163 | } 164 | items = append(items, m) 165 | } 166 | 167 | // TODO: Paging result 168 | return c.JSON(200, items) 169 | 170 | }) 171 | return nil 172 | }) 173 | return nil 174 | } 175 | 176 | func deleteCollection(app *pocketbase.PocketBase, target string) error { 177 | if _, err := app.Dao().DB(). 178 | NewQuery("DELETE FROM " + target + "_embeddings;"). 179 | Execute(); err != nil { 180 | return err 181 | } 182 | if _, err := app.Dao().DB(). 183 | NewQuery("DROP TABLE IF EXISTS " + target + "_embeddings;"). 184 | Execute(); err != nil { 185 | return err 186 | } 187 | return nil 188 | } 189 | 190 | func modelDelete(app *pocketbase.PocketBase, target string, e *core.ModelEvent) error { 191 | _, err := e.Dao.FindRecordById(e.Model.TableName(), e.Model.GetId()) 192 | if err != nil { 193 | return err 194 | } 195 | deleteEmbeddingsForRecord(app, target, e) 196 | return nil 197 | } 198 | 199 | func modelModify(app *pocketbase.PocketBase, target string, client *genai.Client, e *core.ModelEvent) error { 200 | record, err := e.Dao.FindRecordById(e.Model.TableName(), e.Model.GetId()) 201 | if err != nil { 202 | return err 203 | } 204 | title := record.GetString("title") 205 | content := record.GetString("content") 206 | 207 | if content != "" { 208 | result, err := googleAiEmbedContent(client, genai.TaskTypeRetrievalDocument, title, genai.Text(content)) 209 | if err != nil { 210 | return err 211 | } 212 | 213 | vector := "" 214 | jsonVec, err := json.Marshal(result) 215 | if err != nil { 216 | vector = "[]" 217 | } else { 218 | vector = string(jsonVec) 219 | } 220 | 221 | deleteEmbeddingsForRecord(app, target, e) 222 | 223 | { 224 | stmt := "INSERT INTO " + target + "_embeddings (embedding) " 225 | stmt += "VALUES ({:embedding});" 226 | res, err := app.DB().NewQuery(stmt).Bind(dbx.Params{ 227 | "embedding": vector, 228 | }).Execute() 229 | if err != nil { 230 | return nil 231 | } 232 | vectorId, err := res.LastInsertId() 233 | if err != nil { 234 | return err 235 | } 236 | record.Set("vector_id", vectorId) 237 | } 238 | 239 | if err := app.Dao().WithoutHooks().SaveRecord(record); err != nil { 240 | return err 241 | } 242 | } 243 | return nil 244 | } 245 | 246 | func deleteEmbeddingsForRecord(app *pocketbase.PocketBase, target string, e *core.ModelEvent) error { 247 | record, err := e.Dao.FindRecordById(e.Model.TableName(), e.Model.GetId()) 248 | if err != nil { 249 | return err 250 | } 251 | 252 | type Meta struct { 253 | Id string `db:"id" json:"id"` 254 | } 255 | vectorId := record.GetInt("vector_id") 256 | items := []*Meta{} 257 | stmt := "SELECT id FROM " + target + "_embeddings " 258 | stmt += "WHERE id = {:id};" 259 | err = app.DB().NewQuery(stmt).Bind(dbx.Params{ 260 | "id": vectorId, 261 | }).All(&items) 262 | if err != nil { 263 | return nil 264 | } 265 | 266 | stmt = "DELETE FROM " + target + "_embeddings " 267 | stmt += "WHERE id = {:id}" 268 | 269 | for _, item := range items { 270 | _, err = app.DB().NewQuery(stmt).Bind(dbx.Params{ 271 | "id": item.Id, 272 | }).Execute() 273 | if err != nil { 274 | return nil 275 | } 276 | } 277 | 278 | return nil 279 | } 280 | 281 | func createCollection(app *pocketbase.PocketBase, target string, extraFields ...*schema.SchemaField) error { 282 | fields := []*schema.SchemaField{ 283 | { 284 | Name: "title", 285 | Type: schema.FieldTypeText, 286 | }, 287 | { 288 | Name: "content", 289 | Required: true, 290 | Type: schema.FieldTypeText, 291 | }, 292 | { 293 | Name: "vector_id", 294 | Type: schema.FieldTypeNumber, 295 | }, 296 | } 297 | for i, field := range extraFields { 298 | options := field.Options 299 | if options != nil { 300 | relationOption, ok := options.(*schema.RelationOptions) 301 | if ok { 302 | colId := relationOption.CollectionId 303 | if strings.HasPrefix(colId, ColPrefix) { 304 | colId = strings.ReplaceAll(colId, ColPrefix, "") 305 | if col, err := app.Dao().FindCollectionByNameOrId(colId); err != nil { 306 | app.Logger().Error(fmt.Sprint(err)) 307 | return err 308 | } else { 309 | relationOption.CollectionId = col.Id 310 | extraFields[i].Options = relationOption 311 | } 312 | } 313 | 314 | } 315 | 316 | } 317 | } 318 | fields = append(fields, extraFields...) 319 | collection := &models.Collection{ 320 | Name: target, 321 | Type: models.CollectionTypeBase, 322 | Schema: schema.NewSchema(fields...), 323 | Indexes: types.JsonArray[string]{ 324 | "CREATE INDEX idx_" + target + " ON " + target + " (title, content);", 325 | }, 326 | } 327 | 328 | if err := app.Dao().SaveCollection(collection); err != nil { 329 | return err 330 | } 331 | 332 | stmt := "CREATE VIRTUAL TABLE IF NOT EXISTS " + target + "_embeddings using vec0( " 333 | stmt += " id INTEGER PRIMARY KEY AUTOINCREMENT, " 334 | stmt += " embedding float[768] " 335 | stmt += ");" 336 | _, err := app.DB().NewQuery(stmt).Execute() 337 | if err != nil { 338 | return nil 339 | } 340 | 341 | return nil 342 | } 343 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= 3 | cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= 4 | cloud.google.com/go/ai v0.8.0 h1:rXUEz8Wp2OlrM8r1bfmpF2+VKqc1VJpafE3HgzRnD/w= 5 | cloud.google.com/go/ai v0.8.0/go.mod h1:t3Dfk4cM61sytiggo2UyGsDVW3RF1qGZaUKDrZFyqkE= 6 | cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE= 7 | cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs= 8 | cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= 9 | cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= 10 | cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= 11 | cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= 12 | cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0= 13 | cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= 14 | cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU= 15 | cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= 16 | cloud.google.com/go/storage v1.41.0 h1:RusiwatSu6lHeEXe3kglxakAmAbfV+rhtPqA6i8RBx0= 17 | cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= 18 | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= 19 | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 20 | github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= 21 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= 22 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 23 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= 24 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= 25 | github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= 26 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= 27 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 28 | github.com/asg017/sqlite-vec-go-bindings v0.1.1 h1:GA9HxVT1dJFDw7QxEHfssaGw/7Vn+ui23nMnoqQ+OCM= 29 | github.com/asg017/sqlite-vec-go-bindings v0.1.1/go.mod h1:A8+cTt/nKFsYCQF6OgzSNpKZrzNo5gQsXBTfsXHXY0Q= 30 | github.com/aws/aws-sdk-go v1.51.11 h1:El5VypsMIz7sFwAAj/j06JX9UGs4KAbAIEaZ57bNY4s= 31 | github.com/aws/aws-sdk-go v1.51.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= 32 | github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= 33 | github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= 34 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= 35 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= 36 | github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= 37 | github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= 38 | github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= 39 | github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= 40 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= 41 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= 42 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 h1:u1KOU1S15ufyZqmH/rA3POkiRH6EcDANHj2xHRzq+zc= 43 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8/go.mod h1:WPv2FRnkIOoDv/8j2gSUsI4qDc7392w5anFB/I89GZ8= 44 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= 45 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= 46 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= 47 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= 48 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= 49 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= 50 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= 51 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= 52 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= 53 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= 54 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= 55 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= 56 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= 57 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= 58 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= 59 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= 60 | github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 h1:sZXIzO38GZOU+O0C+INqbH7C2yALwfMWpd64tONS/NE= 61 | github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= 62 | github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= 63 | github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= 64 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= 65 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= 66 | github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= 67 | github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= 68 | github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= 69 | github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= 70 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 71 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 72 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 73 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 74 | github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= 75 | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 76 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 77 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 78 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 79 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 80 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 81 | github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= 82 | github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 83 | github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8= 84 | github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= 85 | github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2 h1:4Ew88p5s9dwIk5/woUyqI9BD89NgZoUNH4/rM/h2UDg= 86 | github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2/go.mod h1:o31y53rb/qiIAONF7w3FHJZRqqP3fzHUr1HqanthByw= 87 | github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf h1:2JoVYP9iko8uuIW33BQafzaylDixXbdXCRw/vCoxL+s= 88 | github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf/go.mod h1:bhGPmCgCCTSRfiMYWjpS46IDo9EUZXlsuUaPXSWGbv0= 89 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 90 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 91 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 92 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 93 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 94 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 95 | github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= 96 | github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 97 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 98 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 99 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 100 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 101 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 102 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 103 | github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= 104 | github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= 105 | github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k= 106 | github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE= 107 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 108 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 109 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 110 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 111 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 112 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= 113 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= 114 | github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= 115 | github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= 116 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 117 | github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= 118 | github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= 119 | github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= 120 | github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 121 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 122 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 123 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 124 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 125 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 126 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 127 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 128 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 129 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 130 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 131 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 132 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 133 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 134 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 135 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 136 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 137 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 138 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 139 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 140 | github.com/google/generative-ai-go v0.17.0 h1:kUmCXUIwJouD7I7ev3OmxzzQVICyhIWAxaXk2yblCMY= 141 | github.com/google/generative-ai-go v0.17.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E= 142 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 143 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 144 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 145 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 146 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 147 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 148 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 149 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 150 | github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0 h1:e+8XbKB6IMn8A4OAyZccO4pYfB3s7bt6azNIPE7AnPg= 151 | github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= 152 | github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= 153 | github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= 154 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 155 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 156 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 157 | github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= 158 | github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= 159 | github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= 160 | github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= 161 | github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= 162 | github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= 163 | github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 164 | github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 165 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= 166 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= 167 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 168 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 169 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 170 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 171 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 172 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 173 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 174 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 175 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 176 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 177 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4= 178 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8= 179 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 180 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 181 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 182 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 183 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 184 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 185 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 186 | github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= 187 | github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 188 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 189 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 190 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 191 | github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= 192 | github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= 193 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 194 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 195 | github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA= 196 | github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs= 197 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 198 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 199 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 200 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 201 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 202 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 203 | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= 204 | github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 205 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 206 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 207 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 208 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 209 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 210 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 211 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 212 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 213 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 214 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 215 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 216 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 217 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 218 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 219 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 220 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 221 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 222 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 223 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 224 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 225 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 226 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0= 227 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU= 228 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= 229 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= 230 | go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= 231 | go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= 232 | go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= 233 | go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= 234 | go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= 235 | go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= 236 | gocloud.dev v0.37.0 h1:XF1rN6R0qZI/9DYjN16Uy0durAmSlf58DHOcb28GPro= 237 | gocloud.dev v0.37.0/go.mod h1:7/O4kqdInCNsc6LqgmuFnS0GRew4XNNYWpA44yQnwco= 238 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 239 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 240 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 241 | golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= 242 | golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= 243 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 244 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 245 | golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= 246 | golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= 247 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 248 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 249 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 250 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 251 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= 252 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 253 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 254 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 255 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 256 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 257 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 258 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 259 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 260 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 261 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 262 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 263 | golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= 264 | golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 265 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 266 | golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= 267 | golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 268 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 269 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 270 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 271 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 272 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 273 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 274 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 275 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 276 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 277 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 278 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 279 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 280 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 281 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 282 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 283 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 284 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 285 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= 286 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 287 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 288 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 289 | golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= 290 | golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 291 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 292 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 293 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 294 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 295 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 296 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 297 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 298 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 299 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 300 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 301 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 302 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 303 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 304 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 305 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 306 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 307 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= 308 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 309 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 310 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 311 | golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= 312 | golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 313 | google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI= 314 | google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8= 315 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 316 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 317 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 318 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 319 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 320 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 321 | google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg= 322 | google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= 323 | google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY= 324 | google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= 325 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a h1:hqK4+jJZXCU4pW7jsAdGOVFIfLHQeV7LaizZKnZ84HI= 326 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= 327 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 328 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 329 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 330 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 331 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 332 | google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= 333 | google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= 334 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 335 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 336 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 337 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 338 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 339 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 340 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 341 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 342 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 343 | google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 344 | google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 345 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 346 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 347 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 348 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 349 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 350 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 351 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 352 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 353 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 354 | modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= 355 | modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= 356 | modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= 357 | modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= 358 | modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= 359 | modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= 360 | modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= 361 | modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= 362 | modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e h1:WPC4v0rNIFb2PY+nBBEEKyugPPRHPzUgyN3xZPpGK58= 363 | modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= 364 | modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= 365 | modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= 366 | modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= 367 | modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= 368 | modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= 369 | modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= 370 | modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= 371 | modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 372 | modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= 373 | modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= 374 | modernc.org/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs= 375 | modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= 376 | modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= 377 | modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= 378 | modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= 379 | modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 380 | --------------------------------------------------------------------------------