diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index fa15f2691..2a01a115a 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -33,7 +33,7 @@ jobs: - run: npm run package # build the binaries - - uses: wangyoucao577/go-release-action@v1.49 + - uses: wangyoucao577/go-release-action@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} goos: ${{ matrix.goos }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c9d75e1f0..7a4e76848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ Notable changes to Mailpit will be documented in this file. +## [v1.18.0] + +### Chore +- Update node dependencies +- Update Go dependencies +- Update go-release-action +- JSON key case-consistency for posted API data (backwards-compatible) +- Remove function duplication - use common tools.InArray() +- Improve tag sorting in web UI, ignore casing +- Replace moment JS library with dayjs +- Auto-update relative received message times + +### Feature +- API endpoint for sending ([#278](https://github.com/axllent/mailpit/issues/278)) +- Set tagging filters via a config file +- Search filter support for auto-tagging +- New search filter prefix `addressed:` includes From, To, Cc, Bcc & Reply-To + + ## [v1.17.1] ### Chore diff --git a/cmd/root.go b/cmd/root.go index 631e6ff25..e9344084e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -127,8 +127,9 @@ func init() { rootCmd.Flags().StringVar(&config.POP3TLSKey, "pop3-tls-key", config.POP3TLSKey, "Optional TLS key for POP3 server - requires pop3-tls-cert") // Tagging - rootCmd.Flags().StringVarP(&config.SMTPCLITags, "tag", "t", config.SMTPCLITags, "Tag new messages matching filters") - rootCmd.Flags().BoolVar(&tools.TagsTitleCase, "tags-title-case", tools.TagsTitleCase, "Convert new tags automatically to TitleCase") + rootCmd.Flags().StringVarP(&config.CLITagsArg, "tag", "t", config.CLITagsArg, "Tag new messages matching filters") + rootCmd.Flags().StringVar(&config.TagsConfig, "tags-config", config.TagsConfig, "Load tags filters from yaml configuration file") + rootCmd.Flags().BoolVar(&tools.TagsTitleCase, "tags-title-case", tools.TagsTitleCase, "TitleCase new tags generated from plus-addresses and X-Tags") // Webhook rootCmd.Flags().StringVar(&config.WebhookURL, "webhook-url", config.WebhookURL, "Send a webhook request for new messages") @@ -283,12 +284,9 @@ func initConfigFromEnv() { config.POP3TLSKey = os.Getenv("MP_POP3_TLS_KEY") // Tagging - if len(os.Getenv("MP_TAG")) > 0 { - config.SMTPCLITags = os.Getenv("MP_TAG") - } - if getEnabledFromEnv("MP_TAGS_TITLE_CASE") { - tools.TagsTitleCase = getEnabledFromEnv("MP_TAGS_TITLE_CASE") - } + config.CLITagsArg = os.Getenv("MP_TAG") + config.TagsConfig = os.Getenv("MP_TAGS_CONFIG") + tools.TagsTitleCase = getEnabledFromEnv("MP_TAGS_TITLE_CASE") // Webhook if len(os.Getenv("MP_WEBHOOK_URL")) > 0 { diff --git a/config/config.go b/config/config.go index 174f6475e..b0d888cdb 100644 --- a/config/config.go +++ b/config/config.go @@ -15,7 +15,6 @@ import ( "github.com/axllent/mailpit/internal/auth" "github.com/axllent/mailpit/internal/logger" "github.com/axllent/mailpit/internal/spamassassin" - "github.com/axllent/mailpit/internal/tools" "gopkg.in/yaml.v3" ) @@ -86,14 +85,17 @@ var ( // BlockRemoteCSSAndFonts used to disable remote CSS & fonts BlockRemoteCSSAndFonts = false - // SMTPCLITags is used to map the CLI args - SMTPCLITags string + // CLITagsArg is used to map the CLI args + CLITagsArg string // ValidTagRegexp represents a valid tag ValidTagRegexp = regexp.MustCompile(`^([a-zA-Z0-9\-\ \_\.]){1,}$`) - // SMTPTags are expressions to apply tags to new mail - SMTPTags []AutoTag + // TagsConfig is a yaml file to pre-load tags + TagsConfig string + + // TagFilters are used to apply tags to new mail + TagFilters []autoTag // SMTPRelayConfigFile to parse a yaml file and store config of relay SMTP server SMTPRelayConfigFile string @@ -162,9 +164,9 @@ var ( ) // AutoTag struct for auto-tagging -type AutoTag struct { - Tag string +type autoTag struct { Match string + Tags []string } // SMTPRelayConfigStruct struct for parsing yaml & storing variables @@ -381,27 +383,13 @@ func VerifyConfig() error { } } - SMTPTags = []AutoTag{} - - if SMTPCLITags != "" { - args := tools.ArgsParser(SMTPCLITags) - - for _, a := range args { - t := strings.Split(a, "=") - if len(t) > 1 { - tag := tools.CleanTag(t[0]) - if !ValidTagRegexp.MatchString(tag) || len(tag) == 0 { - return fmt.Errorf("[tag] invalid tag (%s) - can only contain spaces, letters, numbers, - & _", tag) - } - match := strings.TrimSpace(strings.ToLower(strings.Join(t[1:], "="))) - if len(match) == 0 { - return fmt.Errorf("[tag] invalid tag match (%s) - no search detected", tag) - } - SMTPTags = append(SMTPTags, AutoTag{Tag: tag, Match: match}) - } else { - return fmt.Errorf("[tag] error parsing tags (%s)", a) - } - } + // load tag filters + TagFilters = []autoTag{} + if err := loadTagsFromArgs(CLITagsArg); err != nil { + return err + } + if err := loadTagsFromConfig(TagsConfig); err != nil { + return err } if SMTPAllowedRecipients != "" { diff --git a/config/tags.go b/config/tags.go new file mode 100644 index 000000000..cb46f2599 --- /dev/null +++ b/config/tags.go @@ -0,0 +1,81 @@ +package config + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/axllent/mailpit/internal/logger" + "github.com/axllent/mailpit/internal/tools" + "gopkg.in/yaml.v3" +) + +type yamlTags struct { + Filters []yamlTag `yaml:"filters"` +} + +type yamlTag struct { + Match string `yaml:"match"` + Tags string `yaml:"tags"` +} + +// Load tags from a configuration from a file, if set +func loadTagsFromConfig(c string) error { + if c == "" { + return nil // not set, ignore + } + + c = filepath.Clean(c) + + if !isFile(c) { + return fmt.Errorf("[tags] configuration file not found or unreadable: %s", c) + } + + data, err := os.ReadFile(c) + if err != nil { + return fmt.Errorf("[tags] %s", err.Error()) + } + + conf := yamlTags{} + + if err := yaml.Unmarshal(data, &conf); err != nil { + return err + } + + if conf.Filters == nil { + return fmt.Errorf("[tags] missing tag: array in %s", c) + } + + for _, t := range conf.Filters { + tags := strings.Split(t.Tags, ",") + TagFilters = append(TagFilters, autoTag{Match: t.Match, Tags: tags}) + } + + logger.Log().Debugf("[tags] loaded %s from config %s", tools.Plural(len(conf.Filters), "tag filter", "tag filters"), c) + + return nil +} + +func loadTagsFromArgs(c string) error { + if c == "" { + return nil // not set, ignore + } + + args := tools.ArgsParser(c) + + for _, a := range args { + t := strings.Split(a, "=") + if len(t) > 1 { + match := strings.TrimSpace(strings.ToLower(strings.Join(t[1:], "="))) + tags := strings.Split(t[0], ",") + TagFilters = append(TagFilters, autoTag{Match: match, Tags: tags}) + } else { + return fmt.Errorf("[tag] error parsing tags (%s)", a) + } + } + + logger.Log().Debugf("[tags] loaded %s from CLI args", tools.Plural(len(args), "tag filter", "tag filters")) + + return nil +} diff --git a/go.mod b/go.mod index 4b067c57a..270cc7252 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.0 toolchain go1.22.1 require ( - github.com/PuerkitoBio/goquery v1.9.1 + github.com/PuerkitoBio/goquery v1.9.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/axllent/semver v0.0.1 github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 @@ -23,7 +23,7 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/tg123/go-htpasswd v1.2.2 - github.com/vanng822/go-premailer v1.20.2 + github.com/vanng822/go-premailer v1.21.0 golang.org/x/net v0.24.0 golang.org/x/text v0.14.0 golang.org/x/time v0.5.0 @@ -59,7 +59,7 @@ require ( golang.org/x/sys v0.19.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect - modernc.org/libc v1.50.2 // indirect + modernc.org/libc v1.50.5 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect diff --git a/go.sum b/go.sum index 3c334b5fe..927748ecc 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,8 @@ github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI= github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= +github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= @@ -31,7 +30,6 @@ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwg github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -121,13 +119,14 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/vanng822/css v1.0.1 h1:10yiXc4e8NI8ldU6mSrWmSWMuyWgPr9DZ63RSlsgDw8= github.com/vanng822/css v1.0.1/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w= -github.com/vanng822/go-premailer v1.20.2 h1:vKs4VdtfXDqL7IXC2pkiBObc1bXM9bYH3Wa+wYw2DnI= -github.com/vanng822/go-premailer v1.20.2/go.mod h1:RAxbRFp6M/B171gsKu8dsyq+Y5NGsUUvYfg+WQWusbE= +github.com/vanng822/go-premailer v1.21.0 h1:qIwX4urphNPO3xa60MGqowmyjzzMtFacJPKNrt1UWFU= +github.com/vanng822/go-premailer v1.21.0/go.mod h1:6Y3H2NzNmK3sFBNgR1ENdfV9hzG8hMzrA1nL/XBbbP4= github.com/vanng822/r2router v0.0.0-20150523112421-1023140a4f30/go.mod h1:1BVq8p2jVr55Ost2PkZWDrG86PiJ/0lxqcXoAcGxvWU= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= @@ -136,23 +135,20 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -161,12 +157,18 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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/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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -193,16 +195,16 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= modernc.org/cc/v4 v4.21.0 h1:D/gLKtcztomvWbsbvBKo3leKQv+86f+DdqEZBBXhnag= modernc.org/cc/v4 v4.21.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.17.0 h1:cX97L5Bv/7PEmyk1oEAD890fQu5/yUQRYeYBsCSnzww= -modernc.org/ccgo/v4 v4.17.0/go.mod h1:keES1eiOIBJhbA5qKrV7ADG3w8DsX8G7jfHAT76riOg= +modernc.org/ccgo/v4 v4.17.3 h1:t2CQci84jnxKw3GGnHvjGKjiNZeZqyQx/023spkk4hU= +modernc.org/ccgo/v4 v4.17.3/go.mod h1:1FCbAtWYJoKuc+AviS+dH+vGNtYmFJqBeRWjmnDWsIg= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8= modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.50.2 h1:I0+3wlRvXmAEjAJvD7BhP1kmKHwkzV0rOcqFcD85u+0= -modernc.org/libc v1.50.2/go.mod h1:Fd8TZdfRorOd1vB0QCtYSHYAuzobS4xS3mhMGUkeVcA= +modernc.org/libc v1.50.5 h1:ZzeUd0dIc/sUtoPTCYIrgypkuzoGzNu6kbEWj2VuEmk= +modernc.org/libc v1.50.5/go.mod h1:rhzrUx5oePTSTIzBgM0mTftwWHK8tiT9aNFUt1mldl0= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= diff --git a/internal/htmlcheck/main.go b/internal/htmlcheck/main.go index fca276e4e..e953102d8 100644 --- a/internal/htmlcheck/main.go +++ b/internal/htmlcheck/main.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/PuerkitoBio/goquery" + "github.com/axllent/mailpit/internal/tools" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" @@ -136,12 +137,12 @@ func (c CanIEmail) getTest(k string) (Warning, error) { var y, n, p float32 for family, stats := range found.Stats { - if len(LimitFamilies) != 0 && !inArray(family, LimitFamilies) { + if len(LimitFamilies) != 0 && !tools.InArray(family, LimitFamilies) { continue } for platform, clients := range stats.(map[string]interface{}) { - if len(LimitPlatforms) != 0 && !inArray(platform, LimitPlatforms) { + if len(LimitPlatforms) != 0 && !tools.InArray(platform, LimitPlatforms) { continue } for version, support := range clients.(map[string]interface{}) { @@ -182,18 +183,6 @@ func (c CanIEmail) getTest(k string) (Warning, error) { return warning, nil } -func inArray(n string, h []string) bool { - n = strings.ToLower(n) - - for _, v := range h { - if strings.ToLower(v) == n { - return true - } - } - - return false -} - // Convert markdown to HTML, stripping
&
func mdToHTML(str string) string { md := []byte(str) diff --git a/internal/htmlcheck/platforms.go b/internal/htmlcheck/platforms.go index ba3919486..42c0c7970 100644 --- a/internal/htmlcheck/platforms.go +++ b/internal/htmlcheck/platforms.go @@ -1,6 +1,10 @@ package htmlcheck -import "sort" +import ( + "sort" + + "github.com/axllent/mailpit/internal/tools" +) // Platforms returns all platforms with their respective email clients func Platforms() (map[string][]string, error) { @@ -19,7 +23,7 @@ func Platforms() (map[string][]string, error) { if !found { data[platform] = []string{} } - if !inArray(niceFamily, c) { + if !tools.InArray(niceFamily, c) { c = append(c, niceFamily) data[platform] = c } diff --git a/internal/storage/database.go b/internal/storage/database.go index a74a2b589..7049af53b 100644 --- a/internal/storage/database.go +++ b/internal/storage/database.go @@ -108,6 +108,8 @@ func InitDB() error { return err } + LoadTagFilters() + dbFile = p dbLastAction = time.Now() diff --git a/internal/storage/messages.go b/internal/storage/messages.go index 1b6cb044a..638267232 100644 --- a/internal/storage/messages.go +++ b/internal/storage/messages.go @@ -71,13 +71,6 @@ func Store(body *[]byte) (string, error) { return "", err } - // extract tags from body matches based on --tag, plus addresses & X-Tags header - tagStr := findTagsInRawMessage(body) + "," + - obj.tagsFromPlusAddresses() + "," + - strings.TrimSpace(env.Root.Header.Get("X-Tags")) - - tagData := uniqueTagsFromString(tagStr) - // begin a transaction to ensure both the message // and data are stored successfully ctx := context.Background() @@ -119,9 +112,23 @@ func Store(body *[]byte) (string, error) { return "", err } - if len(tagData) > 0 { - // set tags after tx.Commit() - if err := SetMessageTags(id, tagData); err != nil { + // extract tags from body matches + rawTags := findTagsInRawMessage(body) + // extract plus addresses tags from enmime.Envelope + plusTags := obj.tagsFromPlusAddresses() + // extract tags from X-Tags header + xTags := tools.SetTagCasing(strings.Split(strings.TrimSpace(env.Root.Header.Get("X-Tags")), ",")) + // extract tags from search matches + searchTags := tagFilterMatches(id) + + // combine all tags into one slice + tags := append(rawTags, plusTags...) + tags = append(tags, xTags...) + // sort and extract only unique tags + tags = sortedUniqueTags(append(tags, searchTags...)) + + if len(tags) > 0 { + if err := SetMessageTags(id, tags); err != nil { return "", err } } @@ -137,7 +144,7 @@ func Store(body *[]byte) (string, error) { c.Attachments = attachments c.Subject = subject c.Size = size - c.Tags = tagData + c.Tags = tags c.Snippet = snippet websockets.Broadcast("new", c) diff --git a/internal/storage/search.go b/internal/storage/search.go index b68ac6981..e3242c816 100644 --- a/internal/storage/search.go +++ b/internal/storage/search.go @@ -294,6 +294,16 @@ func searchQueryBuilder(searchString, timezone string) *sqlf.Stmt { q.Where("ReplyToJSON LIKE ?", "%"+escPercentChar(w)+"%") } } + } else if strings.HasPrefix(lw, "addressed:") { + w = cleanString(w[10:]) + arg := "%" + escPercentChar(w) + "%" + if w != "" { + if exclude { + q.Where("(ToJSON NOT LIKE ? AND FromJSON NOT LIKE ? AND CcJSON NOT LIKE ? AND BccJSON NOT LIKE ? AND ReplyToJSON NOT LIKE ?)", arg, arg, arg, arg, arg) + } else { + q.Where("(ToJSON LIKE ? OR FromJSON LIKE ? OR CcJSON LIKE ? OR BccJSON LIKE ? OR ReplyToJSON LIKE ?)", arg, arg, arg, arg, arg) + } + } } else if strings.HasPrefix(lw, "subject:") { w = w[8:] if w != "" { diff --git a/internal/storage/tagfilters.go b/internal/storage/tagfilters.go new file mode 100644 index 000000000..2990235ef --- /dev/null +++ b/internal/storage/tagfilters.go @@ -0,0 +1,84 @@ +package storage + +import ( + "context" + "database/sql" + "strings" + + "github.com/axllent/mailpit/config" + "github.com/axllent/mailpit/internal/logger" + "github.com/axllent/mailpit/internal/tools" + "github.com/leporo/sqlf" +) + +// TagFilter struct +type TagFilter struct { + Match string + SQL *sqlf.Stmt + Tags []string +} + +var tagFilters = []TagFilter{} + +// LoadTagFilters loads tag filters from the config and pre-generates the SQL query +func LoadTagFilters() { + tagFilters = []TagFilter{} + + for _, t := range config.TagFilters { + match := strings.TrimSpace(t.Match) + if match == "" { + logger.Log().Warnf("[tags] ignoring tag item with missing 'match'") + continue + } + if t.Tags == nil || len(t.Tags) == 0 { + logger.Log().Warnf("[tags] ignoring tag items with missing 'tags' array") + continue + } + + validTags := []string{} + for _, tag := range t.Tags { + tagName := tools.CleanTag(tag) + if !config.ValidTagRegexp.MatchString(tagName) || len(tagName) == 0 { + logger.Log().Warnf("[tags] invalid tag (%s) - can only contain spaces, letters, numbers, - & _", tagName) + continue + } + validTags = append(validTags, tagName) + } + + if len(validTags) == 0 { + continue + } + + tagFilters = append(tagFilters, TagFilter{Match: match, Tags: validTags, SQL: searchQueryBuilder(match, "")}) + } +} + +// TagFilterMatches returns a slice of matching tags from a message +func tagFilterMatches(id string) []string { + tags := []string{} + + if len(tagFilters) == 0 { + return tags + } + + for _, f := range tagFilters { + var matchID string + q := f.SQL.Clone().Where("ID = ?", id) + if err := q.QueryAndClose(context.Background(), db, func(row *sql.Rows) { + var ignore sql.NullString + + if err := row.Scan(&ignore, &matchID, &ignore, &ignore, &ignore, &ignore, &ignore, &ignore, &ignore, &ignore, &ignore, &ignore, &ignore, &ignore); err != nil { + logger.Log().Errorf("[db] %s", err.Error()) + return + } + }); err != nil { + logger.Log().Errorf("[db] %s", err.Error()) + return tags + } + if matchID == id { + tags = append(tags, f.Tags...) + } + } + + return tags +} diff --git a/internal/storage/tags.go b/internal/storage/tags.go index 8b70cb6a5..9facfc4fe 100644 --- a/internal/storage/tags.go +++ b/internal/storage/tags.go @@ -1,6 +1,7 @@ package storage import ( + "bytes" "context" "database/sql" "regexp" @@ -19,12 +20,12 @@ var ( addTagMutex sync.RWMutex ) -// SetMessageTags will set the tags for a given database ID +// SetMessageTags will set the tags for a given database ID, removing any not in the array func SetMessageTags(id string, tags []string) error { applyTags := []string{} for _, t := range tags { t = tools.CleanTag(t) - if t != "" && config.ValidTagRegexp.MatchString(t) && !inArray(t, applyTags) { + if t != "" && config.ValidTagRegexp.MatchString(t) && !tools.InArray(t, applyTags) { applyTags = append(applyTags, t) } } @@ -33,8 +34,7 @@ func SetMessageTags(id string, tags []string) error { origTagCount := len(currentTags) for _, t := range applyTags { - t = tools.CleanTag(t) - if t == "" || !config.ValidTagRegexp.MatchString(t) || inArray(t, currentTags) { + if t == "" || !config.ValidTagRegexp.MatchString(t) || tools.InArray(t, currentTags) { continue } @@ -47,7 +47,7 @@ func SetMessageTags(id string, tags []string) error { currentTags = getMessageTags(id) for _, t := range currentTags { - if !inArray(t, applyTags) { + if !tools.InArray(t, applyTags) { if err := DeleteMessageTag(id, t); err != nil { return err } @@ -74,14 +74,15 @@ func AddMessageTag(id, name string) error { addTagMutex.Unlock() // check message does not already have this tag var count int - if _, err := sqlf.From(tenant("message_tags")). + + if err := sqlf.From(tenant("message_tags")). Select("COUNT(ID)").To(&count). Where("ID = ?", id). Where("TagID = ?", tagID). - ExecAndClose(context.TODO(), db); err != nil { + QueryRowAndClose(context.Background(), db); err != nil { return err } - if count != 0 { + if count > 0 { // already exists return nil } @@ -213,26 +214,28 @@ func pruneUnusedTags() error { return nil } -// Find tags set via --tags in raw message. +// Find tags set via --tags in raw message, useful for matching all headers etc. +// This function is largely superseded by the database searching, however this +// includes literally everything and is kept for backwards compatibility. // Returns a comma-separated string. -func findTagsInRawMessage(message *[]byte) string { - tagStr := "" - if len(config.SMTPTags) == 0 { - return tagStr +func findTagsInRawMessage(message *[]byte) []string { + tags := []string{} + if len(tagFilters) == 0 { + return tags } - str := strings.ToLower(string(*message)) - for _, t := range config.SMTPTags { - if strings.Contains(str, t.Match) { - tagStr += "," + t.Tag + str := bytes.ToLower(*message) + for _, t := range tagFilters { + if bytes.Contains(str, []byte(t.Match)) { + tags = append(tags, t.Tags...) } } - return tagStr + return tags } // Returns tags found in email plus addresses (eg: test+tagname@example.com) -func (d DBMailSummary) tagsFromPlusAddresses() string { +func (d DBMailSummary) tagsFromPlusAddresses() []string { tags := []string{} for _, c := range d.To { matches := addressPlusRe.FindAllStringSubmatch(c.Address, 1) @@ -257,7 +260,7 @@ func (d DBMailSummary) tagsFromPlusAddresses() string { tags = append(tags, strings.Split(matches[0][2], "+")...) } - return strings.Join(tags, ",") + return tools.SetTagCasing(tags) } // Get message tags from the database for a given database ID @@ -282,24 +285,27 @@ func getMessageTags(id string) []string { return tags } -// UniqueTagsFromString will split a string with commas, and extract a unique slice of formatted tags -func uniqueTagsFromString(s string) []string { +// SortedUniqueTags will return a unique slice of normalised tags +func sortedUniqueTags(s []string) []string { tags := []string{} + added := make(map[string]bool) - if s == "" { + if len(s) == 0 { return tags } - parts := strings.Split(s, ",") - for _, p := range parts { + for _, p := range s { w := tools.CleanTag(p) if w == "" { continue } + lc := strings.ToLower(w) + if _, exists := added[lc]; exists { + continue + } if config.ValidTagRegexp.MatchString(w) { - if !inArray(w, tags) { - tags = append(tags, w) - } + added[lc] = true + tags = append(tags, w) } else { logger.Log().Debugf("[tags] ignoring invalid tag: %s", w) } diff --git a/internal/storage/utils.go b/internal/storage/utils.go index 64583c8a2..fb63493a9 100644 --- a/internal/storage/utils.go +++ b/internal/storage/utils.go @@ -87,18 +87,6 @@ func isFile(path string) bool { return true } -// Tests if a string is within an array. It is not case sensitive. -func inArray(k string, arr []string) bool { - k = strings.ToLower(k) - for _, v := range arr { - if strings.ToLower(v) == k { - return true - } - } - - return false -} - // Convert `%` to `%%` for SQL searches func escPercentChar(s string) string { return strings.ReplaceAll(s, "%", "%%") diff --git a/internal/tools/tags.go b/internal/tools/tags.go index 8496e065f..a43f16dc1 100644 --- a/internal/tools/tags.go +++ b/internal/tools/tags.go @@ -19,18 +19,29 @@ var ( TagsTitleCase bool ) -// CleanTag returns a clean tag, removing whitespace and invalid characters +// CleanTag returns a clean tag, trimming whitespace and replacing invalid characters func CleanTag(s string) string { - s = strings.TrimSpace( + return strings.TrimSpace( multiSpaceRe.ReplaceAllString( tagsInvalidChars.ReplaceAllString(s, " "), " ", ), ) +} + +// SetTagCasing returns the slice of tags, title-casing if set +func SetTagCasing(s []string) []string { + if !TagsTitleCase { + return s + } + + titleTags := []string{} + + c := cases.Title(language.Und, cases.NoLower) - if TagsTitleCase { - return cases.Title(language.Und, cases.NoLower).String(s) + for _, t := range s { + titleTags = append(titleTags, c.String(t)) } - return s + return titleTags } diff --git a/internal/tools/utils.go b/internal/tools/utils.go new file mode 100644 index 000000000..2064f6fa4 --- /dev/null +++ b/internal/tools/utils.go @@ -0,0 +1,26 @@ +package tools + +import ( + "fmt" + "strings" +) + +// Plural returns a singular or plural of a word together with the total +func Plural(total int, singular, plural string) string { + if total == 1 { + return fmt.Sprintf("%d %s", total, singular) + } + return fmt.Sprintf("%d %s", total, plural) +} + +// InArray tests if a string is within an array. It is not case sensitive. +func InArray(k string, arr []string) bool { + k = strings.ToLower(k) + for _, v := range arr { + if strings.ToLower(v) == k { + return true + } + } + + return false +} diff --git a/package-lock.json b/package-lock.json index cb75ea35c..be106d0f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "bootstrap-icons": "^1.9.1", "bootstrap5-tags": "^1.6.1", "color-hash": "^2.0.2", + "dayjs": "^1.11.10", "modern-screenshot": "^4.4.30", - "moment": "^2.29.4", "prismjs": "^1.29.0", "rapidoc": "^9.3.4", "timezones-list": "^3.0.3", @@ -41,9 +41,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -52,9 +52,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.4.tgz", - "integrity": "sha512-VOQOexSilscN24VEY810G/PqtpFvx/z6UqDIjIWbDe2368HhDLkYN5TYwaEz/+eRCUkhJ2WaNLLmQAlxzfWj4w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.5.tgz", + "integrity": "sha512-GWO0mgzNMLWaSYM4z4NVIuY0Cd1fl8cPnuetuddu5w/qGuvt5Y7oUi/kvvQGK9xgOkFJDQX2heIvTRn/OQ1XTg==", "dependencies": { "core-js-pure": "^3.30.2", "regenerator-runtime": "^0.14.0" @@ -466,30 +466,30 @@ } }, "node_modules/@swagger-api/apidom-ast": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-0.99.1.tgz", - "integrity": "sha512-evkKm2JaqNfg3dB2Yk3FWL/Qy2r4csZLMZ9bHMG+xNpti8ulENHMjuCh3Ry4koV1gD7IA54CU2ZjcaTvqJa22Q==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-0.99.2.tgz", + "integrity": "sha512-poNlXWAU2XBl192+lo5sC6loB3qGvwK30V1pta6Hs200KeTayVsMMRL4R6wDDYEtsbv7M3vQaFKcRGbYUk/SgA==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", "@swagger-api/apidom-error": "^0.99.0", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "unraw": "^3.0.0" } }, "node_modules/@swagger-api/apidom-core": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-0.99.1.tgz", - "integrity": "sha512-oWU9Re2B7hPFAnm4ymN2HNOqevMqZsvL4Fjud2qN+KFWNvZ1/r8kwQaj0Pba5Kwka2bcWo0aEfWNayP4axTB+Q==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-0.99.2.tgz", + "integrity": "sha512-deudG9eCxqgPnZyIcZzpmDxF0cja0hdPFS2hB0Op6aB4TKc9mOP1+1iEIDI3Tlx/nzgIayyAl1bblyhK3yH5fQ==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.99.1", + "@swagger-api/apidom-ast": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", "@types/ramda": "~0.29.6", "minim": "~0.23.8", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "short-unique-id": "^5.0.2", "stampit": "^4.3.2" } @@ -503,409 +503,409 @@ } }, "node_modules/@swagger-api/apidom-json-pointer": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-0.99.1.tgz", - "integrity": "sha512-4fOOKTLoBWpfX2eGNx93sqBsS1KRCtBFOq75n1jMcRbs1rrj+JxcaiTFUE+6BZqIqBsCqTmRMYE/HsgwBS3vhQ==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-0.99.2.tgz", + "integrity": "sha512-bZENmE3H2si1yP38VLUAdhoMWNxkh98+/dCOESaw3R5zXHG04di3ShbYsCG0StkigF+eCfCdaj6XoikQOGSkiA==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-ns-api-design-systems": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-0.99.1.tgz", - "integrity": "sha512-LID3n+Y2eKBzaR7oYShto48+EFPBLZLuKIJdEZ53is6SqD5jHS0Ev6xLj2QfqSIQR3OoVN3PUOrz724Jkpiv/A==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-0.99.2.tgz", + "integrity": "sha512-854ioZ/FB5DNiJcMinD9/a6dj6h/poOsKcb4POhPTzMSM0fHLIQUp//Ufhx7qL6qsepwtLapkgZ3/hAYN7lnBg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", - "@swagger-api/apidom-ns-openapi-3-1": "^0.99.1", + "@swagger-api/apidom-ns-openapi-3-1": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-asyncapi-2": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-0.99.1.tgz", - "integrity": "sha512-fAUsKbg0MuvEPjE2UWQu+62K0eh/3yTE2M5u/QCqpj48IpByMNYLKU9ICfMMAzBjXNQAVuEr07/UgY9CRHUVhA==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-0.99.2.tgz", + "integrity": "sha512-HF38kCszKYQqhQ6VMEMqd5r7gPGBRpHwPcoYaRJSDeOST/qLLG78xpoCJKQEyL3PQprea0gXKz1LG1uslDHgtQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-json-schema-draft-7": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-json-schema-draft-7": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-0.99.1.tgz", - "integrity": "sha512-HdxD4WXnaMJsdodrWoynzgteg9UDaZsVkX04oObQPR3C1ZWW9KahEGBSbtr/oBhnE/QgiPfNHUDWrQvk3oC6lg==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-0.99.2.tgz", + "integrity": "sha512-vgCRaqDLI/SmTECZeKO47RGFFx6MCpOcbSm60sV0/ZJxeK+TgkNjIRJTyuRQNts44K863CWgY+bwzzn1zhNqUg==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.99.1", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-ast": "^0.99.2", + "@swagger-api/apidom-core": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-6": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-0.99.1.tgz", - "integrity": "sha512-O6A25j9y+Hjvwwq8x+uTaIhK4tp0CqO6YrFRXmfmOnkBtJ6Q66jqbvRzIN9XQfW8VaIipqAlOin++ufsfuDd1g==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-0.99.2.tgz", + "integrity": "sha512-ayKGsd65a6p/k4s5L2el+vMoMi8kc/bLXVszWszFDET1eZNvhKwEMLylGzKMfnwAFgpj+kJOKn4MZsD6PK6U/A==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", - "@swagger-api/apidom-ns-json-schema-draft-4": "^0.99.1", + "@swagger-api/apidom-ns-json-schema-draft-4": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-7": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-0.99.1.tgz", - "integrity": "sha512-I4IpTkAlParfUWOi5kJU7jQqeMKy39JOWiRz8jTyPoZ8vvixVgyIlOS7/bj5uLxbBw3QxOFXPuIqUvK1uFElAg==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-0.99.2.tgz", + "integrity": "sha512-Rn2YeQKxj6hSijQAzGRRxMYDRIedqHjE69z9xigVbvm+iDXxLJIwasuzFa7BIMRDZF5eAJkBPHXTiU9cXVsl6w==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", - "@swagger-api/apidom-ns-json-schema-draft-6": "^0.99.1", + "@swagger-api/apidom-ns-json-schema-draft-6": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" } }, "node_modules/@swagger-api/apidom-ns-openapi-2": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-0.99.1.tgz", - "integrity": "sha512-ChEd1RaJKrYskLTmlH8NL9tNpAgroSPklTwJCvHmZjzaWvW7N/B2diHBOaz+rnVLiW9Hb7QOlR/biEXJn7OUIg==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-0.99.2.tgz", + "integrity": "sha512-4YlBvMkxSJIWrOQmsHiVuQ2VkbcWgUnOm7uiRq+8d88ur9mKI5XbP5iUvxCASuONmCqlaSU2+qoM1qesy73XPw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", - "@swagger-api/apidom-ns-json-schema-draft-4": "^0.99.1", + "@swagger-api/apidom-ns-json-schema-draft-4": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-openapi-3-0": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-0.99.1.tgz", - "integrity": "sha512-9lfa2a+4rLp+1loEXrr+Dq3whdBwBWHukctsX/C/cGr4SG0NO8+tmS3FLsOD+ly6O/YPdszPDxVcIqqNV8J2uA==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-0.99.2.tgz", + "integrity": "sha512-fcT597Ty3kqTkoBr1jeZ3Lfbu0a+CKd1l2ojY6RBF/5+dWNux+CRZ9qosax2XZbN+nJhSdvGLLvGvuKaV3Ybug==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", - "@swagger-api/apidom-ns-json-schema-draft-4": "^0.99.1", + "@swagger-api/apidom-ns-json-schema-draft-4": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-openapi-3-1": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-0.99.1.tgz", - "integrity": "sha512-XsRxM9WC+WywBo+rr/YUayQRsV2mN8AzBxVlKzJoZ+pBgmPYe24n3Ma/0FTr8zGwQyg4DtOBwydlYz8QFrLPFA==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-0.99.2.tgz", + "integrity": "sha512-ubO8vi1dYpIV2a3IKhTkBCf125udoCeUZIc9wrhOFwwHHIKeInGR5L6yxlNhOQm0/doYCth77vEqcuTBpxaIrw==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.99.1", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-0": "^0.99.1", + "@swagger-api/apidom-ast": "^0.99.2", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-0": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-workflows-1": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-workflows-1/-/apidom-ns-workflows-1-0.99.1.tgz", - "integrity": "sha512-s6SmFzlBmKKRdlyLdZsjXHYJ+7+AuDyK3qrBAPHX7mDe/uN6D7QPGD05oCzHytPhbeZQPMf0wi9vPUrM1s1xvw==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-workflows-1/-/apidom-ns-workflows-1-0.99.2.tgz", + "integrity": "sha512-lm8G7cbCRXukN4UOb/bPszUiSbvN1ymvwQ2PEkyZN+DzJvYfgRuAxXt7xd2EDKJcxeH4igpAnkKoIoBoSOHg+w==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-1": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-1": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-0.99.1.tgz", - "integrity": "sha512-ONeGsOZPZ16SvYbfHKiLjg8IeKGg+nJC+fOIqnelGdMCu/34ed0X7k6XQZGrwbDtmSd3SkXykL3F55H5BFiUPQ==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-0.99.2.tgz", + "integrity": "sha512-7WPbiUJEWggVmxsssFfW/8JGk8Yu4C9ELneh805kMsgl/DOm6hcHxqT5gXXSwamH0ZQlTmSnHl2OZSlG+U5KKQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-api-design-systems": "^0.99.1", - "@swagger-api/apidom-parser-adapter-json": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-api-design-systems": "^0.99.2", + "@swagger-api/apidom-parser-adapter-json": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-yaml": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-0.99.1.tgz", - "integrity": "sha512-mVOHebofGhI3E8HW/7YsqGOpIWOBSMc5R5aQFMYMYpTxrpDHNhyEfFEWqZRAoC2Hin9NZ2BeI/hsrXGIw/LoeQ==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-0.99.2.tgz", + "integrity": "sha512-ezOA1fjBAQPQ5X0DGYnuFyZMBSBCsaT6k9KDRr7B37Do9yj8YKa/lTlg5usXOrcLm4VgcyJGTKhAJi9kfzCKcA==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-api-design-systems": "^0.99.1", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-api-design-systems": "^0.99.2", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-2": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-0.99.1.tgz", - "integrity": "sha512-2kKVf5ecTuDirPpk8nDRyTrT0tkrWjdaUPwJ/+l2RdgWYObNVwdX2lAS9URC4zK/drdQOQxjetF+aDQBBhXmXA==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-0.99.2.tgz", + "integrity": "sha512-b1ncaIc4dD0FGqty3iRCDUA/uHdd7nH271C06blQ+S9Id4D/xXxzd84z8LeNIJNLhCcnueuMKgUkGzvXP+raAA==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-asyncapi-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-json": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-asyncapi-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-json": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-0.99.1.tgz", - "integrity": "sha512-UX+rLOUSQuWe5yNXS8eLFvDhCA1CP5r80jLtvT3n0FDnss4+9WkPlqgj4UPH4XoitXSvBVOZxbdjNwfKtJzsHA==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-0.99.2.tgz", + "integrity": "sha512-NuwuwdORyZPhEpxwyEgslyGfVnwIuyDvF5TDT0cLCMOIFDqbE/n77c4FAh/nQUARDEXRthiDb5pdMo/+rOxjFg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-asyncapi-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-asyncapi-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-json": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-0.99.1.tgz", - "integrity": "sha512-qVeSdhaDIggIkFtMI4aqqv4MYuJlRQ6pniP+Li+DjcHeTKYHelX0OwoznaTlLlZ1tM9QFaMi8rw8xfGp6vMHgg==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-0.99.2.tgz", + "integrity": "sha512-wy2WF71bLX1wEJkgmPRCEnXicV155KCelPQhCtzAGGo/B3+OuhknovBWXZNStvoJqZ/2A4a5pvYrgHoVoIKchg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.99.1", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-ast": "^0.99.2", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "tree-sitter": "=0.20.4", "tree-sitter-json": "=0.20.2", "web-tree-sitter": "=0.20.3" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-2": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-0.99.1.tgz", - "integrity": "sha512-aHzdast9HMeGTaTUWwVovMcspEVCAdvBJe47BzMZfzcVOnZlAVyTmLqxQ/3s9fjseRrPhFYqKtCOKROzbWeAhg==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-0.99.2.tgz", + "integrity": "sha512-z+ATszNWaO2JlixM9h4QpTAW2fE5nPCY4IDcScuWbch8gtKBmv61+53nahYb7tc3W/X0mMqhc1LyTCy5QC2L/w==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-json": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-json": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-0": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-0.99.1.tgz", - "integrity": "sha512-l/nYccP87GL611W9OCiYWUOizhhoGenuKa7Ocmaf9Rg+xIDnPw29+9p/SuGEN2jjtql0iYuNI4+ZzwiC2+teSg==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-0.99.2.tgz", + "integrity": "sha512-78PFDsF67tWDjPCGAD9cNHage8p5Vs2+zili1AF2zch3JkJA/KxBt+5va4A8w1fYaUaXi8LnMkM8VvEIAsNaOw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-0": "^0.99.1", - "@swagger-api/apidom-parser-adapter-json": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-0": "^0.99.2", + "@swagger-api/apidom-parser-adapter-json": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-1": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-0.99.1.tgz", - "integrity": "sha512-Eie4ztKR5hgrGESBDHB9xIODTB/gvjWBwPNveZ/iSlJ/yhZGyDMC8dgv0aQiyFP01mKaaBMhyZjWgsvts9l+cQ==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-0.99.2.tgz", + "integrity": "sha512-WQmm14C0EH0dcMzvgrGPeLkWKXyFwyunK9rrRt7xRLn8sL1Em0dC31hiVdgypo3DLrz9YW3PStpSQjEedJaWUQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-json": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-json": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-2": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-0.99.1.tgz", - "integrity": "sha512-MzjUyhGmJ+jQly90Nak7s01x2Jp1GvBe+Z8BXwkArNOFjLvzQIjdAx7F943/VlLaV9y71DNXVsqhgKdiqjnX3w==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-0.99.2.tgz", + "integrity": "sha512-rEoE54T8KKRxtdxXgvaYba+GX8853mwcw5nzdrrvOy2tNKqsJANPeJcrQmjVYqJX7SU0HuZPK3zBvyqMyKoNsg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-0.99.1.tgz", - "integrity": "sha512-TF/yquy1Alce/olQzR5AnjnOx7o7q8MkXMi0JxrtqvMk9Ky//0qFxFGzFQEzA++NaSGt9StG0Pcgp4MGZAzJYg==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-0.99.2.tgz", + "integrity": "sha512-l7ve45cfAj+imE8flypjdo49zpfp0m29stpOO/q2fCD5/46wT3Z4Ve3aKhil8/TRFEX26VOKoYVNjpeUWzUMaw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-0": "^0.99.1", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-0": "^0.99.2", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-0.99.1.tgz", - "integrity": "sha512-baXbKqjnbmgEmFgCVHlDEiFANHs5lHnnBM0X3k5kNtAVule6Lc5lAZVoySpTGyBJ+4nq4RHNJfbKW8RDHgVMoQ==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-0.99.2.tgz", + "integrity": "sha512-1ab06o/M6MAJ0Js4C1bifpj/R0T0mw26Qk4dR7qKzel9dDuEkIRMQF7JHnf2pojZE+aR59Eb4iAMKmxzokHZdA==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-workflows-json-1": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-json-1/-/apidom-parser-adapter-workflows-json-1-0.99.1.tgz", - "integrity": "sha512-Uu8SaQfl2XiiXDQVRUvUCu3yk7jwHVmwKOoacbJGzPducrR/7/bOe8dNeN4CMRw7HKeRbh02UxXtR46mgBPnog==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-json-1/-/apidom-parser-adapter-workflows-json-1-0.99.2.tgz", + "integrity": "sha512-VsFVmwTX/OfsXyBmIEp5Y+adqBF4Cj/cM/55KPM3mIEmKbc+PK3M08TIotMk1FdCiTafe+I28OZL+WMVujNm1A==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-workflows-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-json": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-workflows-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-json": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-workflows-yaml-1": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-yaml-1/-/apidom-parser-adapter-workflows-yaml-1-0.99.1.tgz", - "integrity": "sha512-9DX9X9wxW6TJF5lG0k/w0GxeMPkHACwEQx/QFJqg1YRD3/UWSkBcm567KbfCh5BiDx5p5WAYhTGInQEAF3d0zQ==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-yaml-1/-/apidom-parser-adapter-workflows-yaml-1-0.99.2.tgz", + "integrity": "sha512-yK+48YcllFc8mY711ZJ7uTfPVZmJdujIHbvGLOMxMODmETkZlEjfoTAwNTWvutcuA6cxK70tKUD8vz5572ALQA==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", - "@swagger-api/apidom-ns-workflows-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", + "@swagger-api/apidom-ns-workflows-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.2", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.0.0" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-0.99.1.tgz", - "integrity": "sha512-MmTDUkrvFIg2AwzaZmiqBifWpoECh7AKeJcAD8Tm+G2/FUmGr3mIr7elc4ehYt/fecSSJEwFGNFU/radKqT/6g==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-0.99.2.tgz", + "integrity": "sha512-eU6Rd58WzzcOYOajwp9UCURhXVO8SUCrau14W6BuF1DbJCr85FmOigy4yu2b9UWsK44ZPzH8KeyhSYwTkqkgLA==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.99.1", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-ast": "^0.99.2", + "@swagger-api/apidom-core": "^0.99.2", "@swagger-api/apidom-error": "^0.99.0", "@types/ramda": "~0.29.6", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "tree-sitter": "=0.20.4", "tree-sitter-yaml": "=0.5.0", "web-tree-sitter": "=0.20.3" } }, "node_modules/@swagger-api/apidom-reference": { - "version": "0.99.1", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-0.99.1.tgz", - "integrity": "sha512-g7xp+ZL/iRX6CEwdUnqqsLfZmaSRlXwEZV8LF1k4k13/o7Qcf7bsPv0fOVGa8ZC29zM8k//FVavwWoXvT2xrFQ==", + "version": "0.99.2", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-0.99.2.tgz", + "integrity": "sha512-QwAnCCEUbicPAVPWYOOpSI8rcj2e7TTybn1chGfdogV+NMLprGXBk/A86hO9CaSLMXkCA2rERUznSNSZWC996g==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.99.1", + "@swagger-api/apidom-core": "^0.99.2", "@types/ramda": "~0.29.6", "axios": "^1.4.0", "minimatch": "^7.4.3", "process": "^0.11.10", - "ramda": "~0.29.1", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "stampit": "^4.3.2" }, "optionalDependencies": { "@swagger-api/apidom-error": "^0.99.0", - "@swagger-api/apidom-json-pointer": "^0.99.1", - "@swagger-api/apidom-ns-asyncapi-2": "^0.99.1", - "@swagger-api/apidom-ns-openapi-2": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-0": "^0.99.1", - "@swagger-api/apidom-ns-openapi-3-1": "^0.99.1", - "@swagger-api/apidom-ns-workflows-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^0.99.1", - "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^0.99.1", - "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-json": "^0.99.1", - "@swagger-api/apidom-parser-adapter-openapi-json-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^0.99.1", - "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "^0.99.1", - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^0.99.1", - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-workflows-json-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-workflows-yaml-1": "^0.99.1", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.1" + "@swagger-api/apidom-json-pointer": "^0.99.2", + "@swagger-api/apidom-ns-asyncapi-2": "^0.99.2", + "@swagger-api/apidom-ns-openapi-2": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-0": "^0.99.2", + "@swagger-api/apidom-ns-openapi-3-1": "^0.99.2", + "@swagger-api/apidom-ns-workflows-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^0.99.2", + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^0.99.2", + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-json": "^0.99.2", + "@swagger-api/apidom-parser-adapter-openapi-json-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^0.99.2", + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "^0.99.2", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^0.99.2", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-workflows-json-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-workflows-yaml-1": "^0.99.2", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.99.2" } }, "node_modules/@types/bootstrap": { @@ -937,36 +937,36 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, "node_modules/@vue/compiler-core": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.24.tgz", - "integrity": "sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.26.tgz", + "integrity": "sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==", "dependencies": { "@babel/parser": "^7.24.4", - "@vue/shared": "3.4.24", + "@vue/shared": "3.4.26", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.24.tgz", - "integrity": "sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz", + "integrity": "sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==", "dependencies": { - "@vue/compiler-core": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/compiler-core": "3.4.26", + "@vue/shared": "3.4.26" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.24.tgz", - "integrity": "sha512-nRAlJUK02FTWfA2nuvNBAqsDZuERGFgxZ8sGH62XgFSvMxO2URblzulExsmj4gFZ8e+VAyDooU9oAoXfEDNxTA==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz", + "integrity": "sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==", "dependencies": { "@babel/parser": "^7.24.4", - "@vue/compiler-core": "3.4.24", - "@vue/compiler-dom": "3.4.24", - "@vue/compiler-ssr": "3.4.24", - "@vue/shared": "3.4.24", + "@vue/compiler-core": "3.4.26", + "@vue/compiler-dom": "3.4.26", + "@vue/compiler-ssr": "3.4.26", + "@vue/shared": "3.4.26", "estree-walker": "^2.0.2", "magic-string": "^0.30.10", "postcss": "^8.4.38", @@ -974,12 +974,12 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.24.tgz", - "integrity": "sha512-ZsAtr4fhaUFnVcDqwW3bYCSDwq+9Gk69q2r/7dAHDrOMw41kylaMgOP4zRnn6GIEJkQznKgrMOGPMFnLB52RbQ==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz", + "integrity": "sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==", "dependencies": { - "@vue/compiler-dom": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/compiler-dom": "3.4.26", + "@vue/shared": "3.4.26" } }, "node_modules/@vue/devtools-api": { @@ -988,48 +988,48 @@ "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==" }, "node_modules/@vue/reactivity": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.24.tgz", - "integrity": "sha512-nup3fSYg4i4LtNvu9slF/HF/0dkMQYfepUdORBcMSsankzRPzE7ypAFurpwyRBfU1i7Dn1kcwpYsE1wETSh91g==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.26.tgz", + "integrity": "sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ==", "dependencies": { - "@vue/shared": "3.4.24" + "@vue/shared": "3.4.26" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.24.tgz", - "integrity": "sha512-c7iMfj6cJMeAG3s5yOn9Rc5D9e2/wIuaozmGf/ICGCY3KV5H7mbTVdvEkd4ZshTq7RUZqj2k7LMJWVx+EBiY1g==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.26.tgz", + "integrity": "sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw==", "dependencies": { - "@vue/reactivity": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/reactivity": "3.4.26", + "@vue/shared": "3.4.26" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.24.tgz", - "integrity": "sha512-uXKzuh/Emfad2Y7Qm0ABsLZZV6H3mAJ5ZVqmAOlrNQRf+T5mxpPGZBfec1hkP41t6h6FwF6RSGCs/gd8WbuySQ==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.26.tgz", + "integrity": "sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==", "dependencies": { - "@vue/runtime-core": "3.4.24", - "@vue/shared": "3.4.24", + "@vue/runtime-core": "3.4.26", + "@vue/shared": "3.4.26", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.24.tgz", - "integrity": "sha512-H+DLK4sQF6sRgzKyofmlEVBIV/9KrQU6HIV7nt6yIwSGGKvSwlV8pqJlebUKLpbXaNHugdSfAbP6YmXF69lxow==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.26.tgz", + "integrity": "sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==", "dependencies": { - "@vue/compiler-ssr": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/compiler-ssr": "3.4.26", + "@vue/shared": "3.4.26" }, "peerDependencies": { - "vue": "3.4.24" + "vue": "3.4.26" } }, "node_modules/@vue/shared": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz", - "integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==" + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.26.tgz", + "integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==" }, "node_modules/anymatch": { "version": "3.1.3", @@ -1324,6 +1324,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/dayjs": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -1941,14 +1946,6 @@ "resolved": "https://registry.npmjs.org/modern-screenshot/-/modern-screenshot-4.4.39.tgz", "integrity": "sha512-p+I4yLZUDnoJMa5zoi+71nLQmoLQ6WRU4W8vZu1BZk2PlIYOz5mGnj9/7t2lGWKYeOr4zo6pajhY0/9TS5Zcdw==" }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "engines": { - "node": "*" - } - }, "node_modules/nan": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", @@ -2177,18 +2174,18 @@ } }, "node_modules/ramda": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", - "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.0.tgz", + "integrity": "sha512-13Y0iMhIQuAm/wNGBL/9HEqIfRGmNmjKnTPlKWfA9f7dnDkr8d45wQ+S7+ZLh/Pq9PdcGxkqKUEA7ySu1QSd9Q==", "funding": { "type": "opencollective", "url": "https://opencollective.com/ramda" } }, "node_modules/ramda-adjunct": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-4.1.1.tgz", - "integrity": "sha512-BnCGsZybQZMDGram9y7RiryoRHS5uwx8YeGuUeDKuZuvK38XO6JJfmK85BwRWAKFA6pZ5nZBO/HBFtExVaf31w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-5.0.0.tgz", + "integrity": "sha512-iEehjqp/ZGjYZybZByDaDu27c+79SE7rKDcySLdmjAwKWkz6jNhvGgZwzUGaMsij8Llp9+1N1Gy0drpAq8ZSyA==", "engines": { "node": ">=0.10.3" }, @@ -2197,7 +2194,7 @@ "url": "https://opencollective.com/ramda-adjunct" }, "peerDependencies": { - "ramda": ">= 0.29.0" + "ramda": ">= 0.30.0" } }, "node_modules/rapidoc": { @@ -2319,9 +2316,9 @@ "optional": true }, "node_modules/sass": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz", - "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.76.0.tgz", + "integrity": "sha512-nc3LeqvF2FNW5xGF1zxZifdW3ffIz5aBb7I7tSvOoNu7z1RQ6pFt9MBuiPtjgaI62YWrM/txjWlOCFiGtf2xpw==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -2336,9 +2333,9 @@ } }, "node_modules/sass-embedded": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.75.0.tgz", - "integrity": "sha512-8ZhQYJSCcjMRClyPpA09ZQ9p0Q9NtYxfMbhifBgUoQZC47Co5QJa0ykhfV/SY6mIqK7aAhMF7NAD5h0MEe2vpg==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.76.0.tgz", + "integrity": "sha512-dPtyZM5f32g/pVx7JjYxbeGyAOmbIAkotHmMMp63z8Nz+tqU6noc7GjGD+jml/w81YObJ7Tom2FvHAj24FNinw==", "dev": true, "peer": true, "dependencies": { @@ -2353,29 +2350,29 @@ "node": ">=16.0.0" }, "optionalDependencies": { - "sass-embedded-android-arm": "1.75.0", - "sass-embedded-android-arm64": "1.75.0", - "sass-embedded-android-ia32": "1.75.0", - "sass-embedded-android-x64": "1.75.0", - "sass-embedded-darwin-arm64": "1.75.0", - "sass-embedded-darwin-x64": "1.75.0", - "sass-embedded-linux-arm": "1.75.0", - "sass-embedded-linux-arm64": "1.75.0", - "sass-embedded-linux-ia32": "1.75.0", - "sass-embedded-linux-musl-arm": "1.75.0", - "sass-embedded-linux-musl-arm64": "1.75.0", - "sass-embedded-linux-musl-ia32": "1.75.0", - "sass-embedded-linux-musl-x64": "1.75.0", - "sass-embedded-linux-x64": "1.75.0", - "sass-embedded-win32-arm64": "1.75.0", - "sass-embedded-win32-ia32": "1.75.0", - "sass-embedded-win32-x64": "1.75.0" + "sass-embedded-android-arm": "1.76.0", + "sass-embedded-android-arm64": "1.76.0", + "sass-embedded-android-ia32": "1.76.0", + "sass-embedded-android-x64": "1.76.0", + "sass-embedded-darwin-arm64": "1.76.0", + "sass-embedded-darwin-x64": "1.76.0", + "sass-embedded-linux-arm": "1.76.0", + "sass-embedded-linux-arm64": "1.76.0", + "sass-embedded-linux-ia32": "1.76.0", + "sass-embedded-linux-musl-arm": "1.76.0", + "sass-embedded-linux-musl-arm64": "1.76.0", + "sass-embedded-linux-musl-ia32": "1.76.0", + "sass-embedded-linux-musl-x64": "1.76.0", + "sass-embedded-linux-x64": "1.76.0", + "sass-embedded-win32-arm64": "1.76.0", + "sass-embedded-win32-ia32": "1.76.0", + "sass-embedded-win32-x64": "1.76.0" } }, "node_modules/sass-embedded-android-arm": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.75.0.tgz", - "integrity": "sha512-3GNCfVEw2D34aEntYHv1+VFb0fOsU2nJdz/kpHXDlE7m/zIsi9ySn9WhvYlXkNQKBXvHRf8mWrU2/mC0QXTxxQ==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.76.0.tgz", + "integrity": "sha512-vcsQZLAoL/+ZiGJK1pnuXtlDOlBTxJPbTp/SpRmODWXucH1f3K3+vbpoqtV6i4/qO5adNuQBWP91MGwtNDiAQA==", "cpu": [ "arm" ], @@ -2393,9 +2390,9 @@ } }, "node_modules/sass-embedded-android-arm64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.75.0.tgz", - "integrity": "sha512-puVKsTovpqntG0b/jjxg6+jWD907UEnc/oJ1ia89KRkvLOPD8kE+EYzxxRYrgaG2tiGSMZNuOmYcAKfQNdLdig==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.76.0.tgz", + "integrity": "sha512-QgGbcNpwxSKX/5y5TtVlD9QQb8UfKEvYE8OjsKkM8iSdeghPcXbvRGwvx87yzTmeLQg3UB2d5u8hb+VOcW2RXg==", "cpu": [ "arm64" ], @@ -2413,9 +2410,9 @@ } }, "node_modules/sass-embedded-android-ia32": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.75.0.tgz", - "integrity": "sha512-SObSy6USALhGQoX/Lu1Gctwsb6Ob4Hkg1ISHSV8SNHeWIko4ZiHbAb1r9UMJtRznyawvZ6fjKgOY5fLJAfk3xQ==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.76.0.tgz", + "integrity": "sha512-qjSPggA6hy3eueQ5HR+5689BcFjSPn23shj9lgeILB/GT5xfoF8Ry+S/srSA7mSIVEeM0BCBvapTpLfpCeEOUw==", "cpu": [ "ia32" ], @@ -2433,9 +2430,9 @@ } }, "node_modules/sass-embedded-android-x64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.75.0.tgz", - "integrity": "sha512-CCroBmmwbVZbwOXzFg6GdbOwcczhtjJ/75cfpAoku0InDJzxCP+sVJz8LL16rLWDsVveDoXX7JKFw9Nb9zQyjQ==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.76.0.tgz", + "integrity": "sha512-+K8GZA30EmvABja4ZqWdUhZ01Qf4tWCiTfPEwzwuQVsBiTLS5g3aQtOkitGmwrt8n070klGmSMgfOiGf1r3f8w==", "cpu": [ "x64" ], @@ -2453,9 +2450,9 @@ } }, "node_modules/sass-embedded-darwin-arm64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.75.0.tgz", - "integrity": "sha512-lb7Wkq69+AfD/tnopRX9RSu3d99Gsu1iIAhs3GyMh2N2AnVooASqKJ6I3IAbKnGh+MkXOISsoeyTP4hSnPyuqw==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.76.0.tgz", + "integrity": "sha512-AEtCkJWQfabvIuu1b1mcvDH0OD99tAtVloUkSNmN83m3lhQeen01/mQ1ZO5LI+qFGbLBSUv3h8Iu+OQL4KyYFw==", "cpu": [ "arm64" ], @@ -2473,9 +2470,9 @@ } }, "node_modules/sass-embedded-darwin-x64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.75.0.tgz", - "integrity": "sha512-Lw02PAS0bY7Q4v2fWxlFUU/T/1AV49H2+Oirxtij5nF8rTgjNHlr/7cOQp/f8bRdG5SnPhJspy//c6K8X7cF9w==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.76.0.tgz", + "integrity": "sha512-9SFxnMCdU3aYFBh3It9bHriLbm4SaEonb2la3GJ4oCJths653VUhMASCXmtgQh4eXjCni/hs8s5QY9YoaDTXqQ==", "cpu": [ "x64" ], @@ -2493,9 +2490,9 @@ } }, "node_modules/sass-embedded-linux-arm": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.75.0.tgz", - "integrity": "sha512-s4yDbv/MEMVWr6E6uk7T/Fh4iX71NQDBDVQ/tbq4+VgF/SAo2YdnW1p97zwJjqwxNgIo76o21HaZO4rs66Ur8A==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.76.0.tgz", + "integrity": "sha512-MyCsst72tcRyCKDs+OV/LbTzOwnyVuGJQcFceXgHde10MlGlfkD4z51umG6mmHntus6IAtmBhfVpqI8AKRCIbw==", "cpu": [ "arm" ], @@ -2513,9 +2510,9 @@ } }, "node_modules/sass-embedded-linux-arm64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.75.0.tgz", - "integrity": "sha512-v1d1Zzje46dXzRFm594RfkwdFnbPz1vlxM5vtLoz2d/r+TijhpVzNyy4dY4Dz0MEIcAU+edzB2P9MIHdX9yP9A==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.76.0.tgz", + "integrity": "sha512-GWgfzULQHNdWQTnBKTZ4wZq6oDteQZPkmDm66FeK9B+Wkhyyy3P1el8bW2A8C4CArzQKvSt7xFbukTltGPPU5g==", "cpu": [ "arm64" ], @@ -2533,9 +2530,9 @@ } }, "node_modules/sass-embedded-linux-ia32": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.75.0.tgz", - "integrity": "sha512-fxpWoX9Bc4rSA863aehnPPibiIRisSBktqKY5vkQnTg4L7SDxPwXMvHeL0LZQOG1t6+baSnjhOUZSjfwbl09MQ==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.76.0.tgz", + "integrity": "sha512-ge7zqyMWTOkJyOgaRRsJwT7dzNaw00y5mZ7QT7n+Nz+plPxyeCUWam8gbJFgEB6+/flrqt/Vaze+RblkwJZY4w==", "cpu": [ "ia32" ], @@ -2553,9 +2550,9 @@ } }, "node_modules/sass-embedded-linux-musl-arm": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.75.0.tgz", - "integrity": "sha512-mVczW2hkGxXAs8I7H+m4bfZZIHd9tFR+UA2mHI+XkLE+cjyPC2FuWokc+WXyaWpqbE/KDSwqgSqY08zND7gcbA==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.76.0.tgz", + "integrity": "sha512-qsWfK57exwmcQRhLfhQ+SJ1TYUVuKI7+x7t1xw8dbC6AfXnRfBXAjmQTWK18sgqUXMhatqikWuxUCGHRdHQBRw==", "cpu": [ "arm" ], @@ -2570,9 +2567,9 @@ } }, "node_modules/sass-embedded-linux-musl-arm64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.75.0.tgz", - "integrity": "sha512-5ZlghwIG82spTNvutXbyXRC2cOMx7TZdWoiEqZ5QXuhChB35AHk43Ex1CdItPOLX0JjRv5eSuSelCY4nBGDe8A==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.76.0.tgz", + "integrity": "sha512-R7zBSLtNyKniX28rZtsA3ybwka9SGrT0H+nkk9iuIQu+bqJUjN4ZL1fCx84x5lWlM3t/v47NU6W9q1LArUKWhA==", "cpu": [ "arm64" ], @@ -2587,9 +2584,9 @@ } }, "node_modules/sass-embedded-linux-musl-ia32": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.75.0.tgz", - "integrity": "sha512-8RjKtvc1F9xP1hr+ht72CawkSr7/fZMSAhE/TORFncsPKpwN2WGqkoTBXdL22WGwi95ZAz5Zr2ZnGy8OXMDprQ==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.76.0.tgz", + "integrity": "sha512-V5OuUX01ZcdkC3UBvLT23+4LnQYAkfZwe1cwBa7BBpbAigTSZnAOTLEj6P7vFCDYUx40NlPpjHlQxB5imFTUpg==", "cpu": [ "ia32" ], @@ -2604,9 +2601,9 @@ } }, "node_modules/sass-embedded-linux-musl-x64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.75.0.tgz", - "integrity": "sha512-bsuOEy6rjIwfc7qihDSrEnmaePUn8bR5NAAzeljlfQkRFRxivB1gysQfRPjLPbheJfChFDLiiFX2z2CQkcdKuQ==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.76.0.tgz", + "integrity": "sha512-KiqDUOIiVTyY8GvpiHSkxmGnOmq7sF51QyOf09gHnLdSmNK8giS1F5SPHE3oA9q3Q2jE4UuEAaUz50qYRUJ5Pw==", "cpu": [ "x64" ], @@ -2621,9 +2618,9 @@ } }, "node_modules/sass-embedded-linux-x64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.75.0.tgz", - "integrity": "sha512-L7x3orLODCRds6PpDfrb6bbh6IdqHDzcwyt6VkcbTN+KtbMI4PfNGKHeo7f2K8wMbCiFK3BGJqMSPxNRuVp19A==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.76.0.tgz", + "integrity": "sha512-vMSZjbUpaf8dbpgcqnSTFYn3nxL0s8MEzFcArdacaQMY9RRempZGkd674aon8aoJw3u+O2VoMsCTXg3BKHoOfw==", "cpu": [ "x64" ], @@ -2641,9 +2638,9 @@ } }, "node_modules/sass-embedded-win32-arm64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.75.0.tgz", - "integrity": "sha512-wdVHtJBVykRWA2YEYsJ1bLf9sjcwa9BhHRzf03nSwS88Vc7GmflY6HyuY2Ynz+dWoth7MelgJL/XlonOs5y6/Q==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.76.0.tgz", + "integrity": "sha512-0iE91pDXqm9rpkpORW9V7gJHvtOueW0xUeJmpaRA+ErMUqYYyLKGFL8KgVqmHU/Eb0/FPFHT34FyeqkcGOliag==", "cpu": [ "arm64" ], @@ -2661,9 +2658,9 @@ } }, "node_modules/sass-embedded-win32-ia32": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.75.0.tgz", - "integrity": "sha512-q/uE8q8PLG7Y7mcP1Lsiwg+6FwShj8dLk76Fa2FB68odLn42/aZ2eDHbpy+bbMgAqZqlcDDsqbCDHF9O3d0KrA==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.76.0.tgz", + "integrity": "sha512-2jvq+mWBNTGt4a9/hXHqov9FpdC4NI5HFLttgNOyAWYHbvgEv9oNj+ZqTwG9zZZacHYhtV8n0+Rb3H4/QQTh8A==", "cpu": [ "ia32" ], @@ -2681,9 +2678,9 @@ } }, "node_modules/sass-embedded-win32-x64": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.75.0.tgz", - "integrity": "sha512-lIT3ziKm2L9XGwP3S1D0Kk9ySJ6lVBLm+GZ2goQi8cAWepHSnmRz+mcd/AEqxDGEvrgNmmmvu3ylwlJ/6Nrm9w==", + "version": "1.76.0", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.76.0.tgz", + "integrity": "sha512-fRDxUQl4o2RaMLcLGn8E6hzELDNT+ATz2cgcFpJ27GIhDJECSeEIYWyFP/SaLLvrWZqo1srVLPC7L2SlHMV9gA==", "cpu": [ "x64" ], @@ -2732,9 +2729,9 @@ } }, "node_modules/short-unique-id": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.0.3.tgz", - "integrity": "sha512-yhniEILouC0s4lpH0h7rJsfylZdca10W9mDJRAFh3EpcSUanCHGb0R7kcFOIUCZYSAPo0PUD5ZxWQdW0T4xaug==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.2.0.tgz", + "integrity": "sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==", "bin": { "short-unique-id": "bin/short-unique-id", "suid": "bin/short-unique-id" @@ -3026,15 +3023,15 @@ "peer": true }, "node_modules/vue": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.24.tgz", - "integrity": "sha512-NPdx7dLGyHmKHGRRU5bMRYVE+rechR+KDU5R2tSTNG36PuMwbfAJ+amEvOAw7BPfZp5sQulNELSLm5YUkau+Sg==", - "dependencies": { - "@vue/compiler-dom": "3.4.24", - "@vue/compiler-sfc": "3.4.24", - "@vue/runtime-dom": "3.4.24", - "@vue/server-renderer": "3.4.24", - "@vue/shared": "3.4.24" + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.26.tgz", + "integrity": "sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg==", + "dependencies": { + "@vue/compiler-dom": "3.4.26", + "@vue/compiler-sfc": "3.4.26", + "@vue/runtime-dom": "3.4.26", + "@vue/server-renderer": "3.4.26", + "@vue/shared": "3.4.26" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index dfcdf3d9d..21ad28e4c 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "bootstrap-icons": "^1.9.1", "bootstrap5-tags": "^1.6.1", "color-hash": "^2.0.2", + "dayjs": "^1.11.10", "modern-screenshot": "^4.4.30", - "moment": "^2.29.4", "prismjs": "^1.29.0", "rapidoc": "^9.3.4", "timezones-list": "^3.0.3", diff --git a/server/apiv1/api.go b/server/apiv1/api.go index 24a3a6647..8f995e10b 100644 --- a/server/apiv1/api.go +++ b/server/apiv1/api.go @@ -666,18 +666,18 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { return } - froms, err := m.Header.AddressList("From") + fromAddresses, err := m.Header.AddressList("From") if err != nil { httpError(w, err.Error()) return } - if len(froms) == 0 { + if len(fromAddresses) == 0 { httpError(w, "No From header found") return } - from := froms[0].Address + from := fromAddresses[0].Address // if sender is used, then change from to the sender if senders, err := m.Header.AddressList("Sender"); err == nil { @@ -900,6 +900,19 @@ func httpError(w http.ResponseWriter, msg string) { fmt.Fprint(w, msg) } +// httpJSONError returns a basic error message (400 response) in JSON format +func httpJSONError(w http.ResponseWriter, msg string) { + w.Header().Set("Referrer-Policy", "no-referrer") + w.Header().Set("Content-Security-Policy", config.ContentSecurityPolicy) + w.WriteHeader(http.StatusBadRequest) + e := JSONErrorMessage{ + Error: msg, + } + bytes, _ := json.Marshal(e) + w.Header().Add("Content-Type", "application/json") + _, _ = w.Write(bytes) +} + // Get the start and limit based on query params. Defaults to 0, 50 func getStartLimit(req *http.Request) (start int, limit int) { start = 0 diff --git a/server/apiv1/send.go b/server/apiv1/send.go new file mode 100644 index 000000000..773338033 --- /dev/null +++ b/server/apiv1/send.go @@ -0,0 +1,275 @@ +package apiv1 + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "net/mail" + "strings" + + "github.com/axllent/mailpit/internal/tools" + "github.com/axllent/mailpit/server/smtpd" + "github.com/jhillyerd/enmime" +) + +// swagger:parameters SendMessage +type sendMessageParams struct { + // in: body + Body *SendRequest +} + +// SendRequest to send a message via HTTP +// swagger:model SendRequest +type SendRequest struct { + // "From" recipient + // required: true + From struct { + // Optional name + // example: John Doe + Name string + // Email address + // example: john@example.com + // required: true + Email string + } + + // "To" recipients + To []struct { + // Optional name + // example: Jane Doe + Name string + // Email address + // example: jane@example.com + // required: true + Email string + } + + // Cc recipients + Cc []struct { + // Optional name + // example: Manager + Name string + // Email address + // example: manager@example.com + // required: true + Email string + } + + // Bcc recipients email addresses only + // example: ["jack@example.com"] + Bcc []string + + // Optional Reply-To recipients + ReplyTo []struct { + // Optional name + // example: Secretary + Name string + // Email address + // example: secretary@example.com + // required: true + Email string + } + + // Subject + // example: Mailpit message via the HTTP API + Subject string + + // Message body (text) + // example: This is the text body + Text string + + // Message body (HTML) + // example:Mailpit is awesome!
+ HTML string + + // Attachments + Attachments []struct { + // Base64-encoded string of the file content + // required: true + // example: VGhpcyBpcyBhIHBsYWluIHRleHQgYXR0YWNobWVudA== + Content string + // Filename + // required: true + // example: AttachedFile.txt + Filename string + } + + // Mailpit tags + // example: ["Tag 1","Tag 2"] + Tags []string + + // Optional headers in {"key":"value"} format + // example: {"X-IP":"1.2.3.4"} + Headers map[string]string +} + +// SendMessageConfirmation struct +type SendMessageConfirmation struct { + // Database ID + // example: iAfZVVe2UQFNSG5BAjgYwa + ID string +} + +// JSONErrorMessage struct +type JSONErrorMessage struct { + // Error message + // example: invalid format + Error string +} + +// SendMessageHandler handles HTTP requests to send a new message +func SendMessageHandler(w http.ResponseWriter, r *http.Request) { + // swagger:route POST /api/v1/send message SendMessage + // + // # Send a message + // + // Send a message via the HTTP API. + // + // Consumes: + // - application/json + // + // Produces: + // - application/json + // + // Schemes: http, https + // + // Responses: + // 200: sendMessageResponse + // default: jsonErrorResponse + + decoder := json.NewDecoder(r.Body) + + data := SendRequest{} + + if err := decoder.Decode(&data); err != nil { + httpJSONError(w, err.Error()) + return + } + + id, err := data.Send(r.RemoteAddr) + + if err != nil { + httpJSONError(w, err.Error()) + return + } + + bytes, _ := json.Marshal(SendMessageConfirmation{ID: id}) + + w.Header().Add("Content-Type", "application/json") + _, _ = w.Write(bytes) +} + +// Send will validate the message structure and attempt to send to Mailpit. +// It returns a sending summary or an error. +func (d SendRequest) Send(remoteAddr string) (string, error) { + ip, _, err := net.SplitHostPort(remoteAddr) + if err != nil { + return "", fmt.Errorf("error parsing request RemoteAddr: %s", err.Error()) + } + + ipAddr := &net.IPAddr{IP: net.ParseIP(ip)} + + addresses := []string{} + + msg := enmime.Builder(). + From(d.From.Name, d.From.Email). + Subject(d.Subject). + Text([]byte(d.Text)) + + if d.HTML != "" { + msg = msg.HTML([]byte(d.HTML)) + } + + if len(d.To) > 0 { + for _, a := range d.To { + if _, err := mail.ParseAddress(a.Email); err == nil { + msg = msg.To(a.Name, a.Email) + addresses = append(addresses, a.Email) + } else { + return "", fmt.Errorf("invalid To address: %s", a.Email) + } + } + } + + if len(d.Cc) > 0 { + for _, a := range d.Cc { + if _, err := mail.ParseAddress(a.Email); err == nil { + msg = msg.CC(a.Name, a.Email) + addresses = append(addresses, a.Email) + } else { + return "", fmt.Errorf("invalid Cc address: %s", a.Email) + } + } + } + + if len(d.Bcc) > 0 { + for _, e := range d.Bcc { + if _, err := mail.ParseAddress(e); err == nil { + msg = msg.BCC("", e) + addresses = append(addresses, e) + } else { + return "", fmt.Errorf("invalid Bcc address: %s", e) + } + } + } + + if len(d.ReplyTo) > 0 { + for _, a := range d.ReplyTo { + if _, err := mail.ParseAddress(a.Email); err == nil { + msg = msg.ReplyTo(a.Name, a.Email) + } else { + return "", fmt.Errorf("invalid Reply-To address: %s", a.Email) + } + } + } + + restrictedHeaders := []string{"To", "From", "Cc", "Bcc", "Reply-To", "Date", "Subject", "Content-Type", "Mime-Version"} + + if len(d.Tags) > 0 { + msg = msg.Header("X-Tags", strings.Join(d.Tags, ", ")) + restrictedHeaders = append(restrictedHeaders, "X-Tags") + } + + if len(d.Headers) > 0 { + for k, v := range d.Headers { + // check header isn't in "restricted" headers + if tools.InArray(k, restrictedHeaders) { + return "", fmt.Errorf("cannot overwrite header: \"%s\"", k) + } + msg = msg.Header(k, v) + } + } + + if len(d.Attachments) > 0 { + for _, a := range d.Attachments { + // workaround: split string because JS readAsDataURL() returns the base64 string + // with the mime type prefix eg: data:image/png;base64,Mailpit is awesome!
", + "Attachments": [ + { + "Content": "VGhpcyBpcyBhIHBsYWluIHRleHQgYXR0YWNobWVudA==", + "Filename": "Attached File.txt" + } + ], + "ReplyTo": [ + { + "Email": "secretary@example.com", + "Name": "Secretary" + } + ], + "Tags": [ + "Tag 1", + "Tag 2" + ] + }` + + t.Log("Sending message via HTTP API") + b, err := clientPost(ts.URL+"/api/v1/send", jsonData) + if err != nil { + t.Errorf("Expected nil, received %s", err.Error()) + } + + resp := apiv1.SendMessageConfirmation{} + + if err := json.Unmarshal(b, &resp); err != nil { + t.Errorf(err.Error()) + return + } + + t.Logf("Fetching response for message %s", resp.ID) + msg, err := fetchMessage(ts.URL + "/api/v1/message/" + resp.ID) + if err != nil { + t.Errorf(err.Error()) + } + + t.Logf("Testing response for message %s", resp.ID) + assertEqual(t, `Mailpit message via the HTTP API`, msg.Subject, "wrong subject") + assertEqual(t, `This is the text body`, msg.Text, "wrong text") + assertEqual(t, `Mailpit is awesome!
`, msg.HTML, "wrong HTML") + assertEqual(t, `"John Doe"