+75
db/storage.go
+75
db/storage.go
···
1
+
package db
2
+
3
+
import (
4
+
"log"
5
+
"time"
6
+
7
+
"github.com/jellydator/ttlcache/v3"
8
+
"github.com/ostafen/clover/v2"
9
+
"go.hacdias.com/indielib/indieauth"
10
+
)
11
+
12
+
type Storage struct {
13
+
Docs *clover.DB
14
+
Authorization *ttlcache.Cache[string, *indieauth.AuthenticationRequest]
15
+
}
16
+
17
+
type CollectionName string
18
+
19
+
var (
20
+
PostCollection CollectionName = "posts"
21
+
)
22
+
23
+
func NewStorage() *Storage {
24
+
c, err := clover.Open("data/docs")
25
+
if err != nil {
26
+
log.Fatal(err)
27
+
}
28
+
29
+
cache := ttlcache.New[string, *indieauth.AuthenticationRequest](
30
+
ttlcache.WithTTL[string, *indieauth.AuthenticationRequest](10 * time.Minute),
31
+
)
32
+
33
+
go cache.Start()
34
+
35
+
store := &Storage{
36
+
Docs: c,
37
+
Authorization: cache,
38
+
}
39
+
40
+
store.SetupClover()
41
+
42
+
return store
43
+
}
44
+
45
+
func (db *Storage) SetupClover() {
46
+
if ok, err := db.Docs.HasCollection(string(PostCollection)); err != nil {
47
+
panic(err)
48
+
} else if !ok {
49
+
err := db.Docs.CreateCollection(string(PostCollection))
50
+
if err != nil {
51
+
panic(err)
52
+
}
53
+
}
54
+
55
+
if ok, err := db.Docs.HasIndex(string(PostCollection), "createdAt"); err != nil {
56
+
panic(err)
57
+
} else if !ok {
58
+
err := db.Docs.CreateIndex(string(PostCollection), "createdAt")
59
+
if err != nil {
60
+
panic(err)
61
+
}
62
+
}
63
+
}
64
+
65
+
func (db *Storage) Cleanup() {
66
+
if err := db.Docs.Close(); err != nil {
67
+
panic(err)
68
+
}
69
+
70
+
db.Authorization.Stop()
71
+
}
72
+
73
+
func (db *Storage) SetupTokenCache() {
74
+
75
+
}
+4
gen.go
+4
gen.go
+49
go.mod
+49
go.mod
···
1
+
module github.com/puregarlic/space
2
+
3
+
go 1.22.5
4
+
5
+
require (
6
+
github.com/aidarkhanov/nanoid v1.0.8
7
+
github.com/jellydator/ttlcache/v3 v3.2.0
8
+
github.com/joho/godotenv v1.5.1
9
+
github.com/ostafen/clover/v2 v2.0.0-alpha.3
10
+
go.hacdias.com/indielib v0.3.1
11
+
)
12
+
13
+
require (
14
+
github.com/cespare/xxhash v1.1.0 // indirect
15
+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
16
+
github.com/dgraph-io/badger/v3 v3.2103.2 // indirect
17
+
github.com/dgraph-io/ristretto v0.1.0 // indirect
18
+
github.com/dustin/go-humanize v1.0.0 // indirect
19
+
github.com/gogo/protobuf v1.3.2 // indirect
20
+
github.com/golang/glog v1.0.0 // indirect
21
+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
22
+
github.com/golang/protobuf v1.5.2 // indirect
23
+
github.com/golang/snappy v0.0.4 // indirect
24
+
github.com/google/flatbuffers v2.0.6+incompatible // indirect
25
+
github.com/google/orderedcode v0.0.1 // indirect
26
+
github.com/klauspost/compress v1.15.9 // indirect
27
+
github.com/pkg/errors v0.9.1 // indirect
28
+
github.com/satori/go.uuid v1.2.0 // indirect
29
+
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
30
+
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
31
+
go.etcd.io/bbolt v1.3.6 // indirect
32
+
go.opencensus.io v0.23.0 // indirect
33
+
golang.org/x/sync v0.7.0 // indirect
34
+
golang.org/x/sys v0.22.0 // indirect
35
+
golang.org/x/text v0.16.0 // indirect
36
+
google.golang.org/protobuf v1.28.1 // indirect
37
+
)
38
+
39
+
require (
40
+
github.com/a-h/templ v0.2.747
41
+
github.com/go-chi/chi/v5 v5.1.0
42
+
github.com/go-chi/httprate v0.12.0
43
+
github.com/golang-jwt/jwt/v5 v5.2.1
44
+
github.com/samber/lo v1.46.0
45
+
golang.org/x/net v0.27.0 // indirect
46
+
golang.org/x/oauth2 v0.21.0 // indirect
47
+
willnorris.com/go/microformats v1.2.1-0.20240301064101-b5d1b9d2120e // indirect
48
+
willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0 // indirect
49
+
)
+267
go.sum
+267
go.sum
···
1
+
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2
+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3
+
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
4
+
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
5
+
github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg=
6
+
github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4=
7
+
github.com/aidarkhanov/nanoid v1.0.8 h1:yxyJkgsEDFXP7+97vc6JevMcjyb03Zw+/9fqhlVXBXA=
8
+
github.com/aidarkhanov/nanoid v1.0.8/go.mod h1:vadfZHT+m4uDhttg0yY4wW3GKtl2T6i4d2Age+45pYk=
9
+
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
10
+
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
11
+
github.com/brianvoe/gofakeit/v6 v6.17.0 h1:obbQTJeHfktJtiZzq0Q1bEpsNUs+yHrYlPVWt7BtmJ4=
12
+
github.com/brianvoe/gofakeit/v6 v6.17.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
13
+
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
14
+
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
15
+
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
16
+
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
17
+
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
18
+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
19
+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
20
+
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
21
+
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
22
+
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
23
+
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
24
+
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
25
+
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
26
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
27
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
28
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
29
+
github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8=
30
+
github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M=
31
+
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
32
+
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
33
+
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
34
+
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
35
+
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
36
+
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
37
+
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
38
+
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
39
+
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
40
+
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
41
+
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
42
+
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
43
+
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
44
+
github.com/go-chi/httprate v0.12.0 h1:08D/te3pOTJe5+VAZTQrHxwdsH2NyliiUoRD1naKaMg=
45
+
github.com/go-chi/httprate v0.12.0/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0=
46
+
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
47
+
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
48
+
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
49
+
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
50
+
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
51
+
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
52
+
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
53
+
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
54
+
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
55
+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
56
+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
57
+
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
58
+
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
59
+
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
60
+
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
61
+
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
62
+
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
63
+
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
64
+
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
65
+
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
66
+
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
67
+
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
68
+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
69
+
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
70
+
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
71
+
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
72
+
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
73
+
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
74
+
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
75
+
github.com/google/flatbuffers v2.0.6+incompatible h1:XHFReMv7nFFusa+CEokzWbzaYocKXI6C7hdU5Kgh9Lw=
76
+
github.com/google/flatbuffers v2.0.6+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
77
+
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
78
+
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
79
+
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
80
+
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
81
+
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
82
+
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
83
+
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
84
+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
85
+
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
86
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
87
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
88
+
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
89
+
github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
90
+
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
91
+
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
92
+
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
93
+
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
94
+
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
95
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
96
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
97
+
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
98
+
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
99
+
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
100
+
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
101
+
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
102
+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
103
+
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
104
+
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
105
+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
106
+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
107
+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
108
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
109
+
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
110
+
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
111
+
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
112
+
github.com/ostafen/clover/v2 v2.0.0-alpha.3 h1:fXC7tVHQkUPFlxlj/kD98h0ngrTpIeJymaxVIqDzw3Q=
113
+
github.com/ostafen/clover/v2 v2.0.0-alpha.3/go.mod h1:5YCDt+wJDUNN1uSXE5csxSQBuJrNjidkOkJTXWuNhDY=
114
+
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
115
+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
116
+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
117
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
118
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
119
+
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
120
+
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
121
+
github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ=
122
+
github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
123
+
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
124
+
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
125
+
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
126
+
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
127
+
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
128
+
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
129
+
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
130
+
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
131
+
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
132
+
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
133
+
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
134
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
135
+
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
136
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
137
+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
138
+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
139
+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
140
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
141
+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
142
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
143
+
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
144
+
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
145
+
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
146
+
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
147
+
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
148
+
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
149
+
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
150
+
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
151
+
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
152
+
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
153
+
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
154
+
go.hacdias.com/indielib v0.3.1 h1:t6IUp2lfQBa3baXcBN9S/A4fBq4vzMWVbfCQgPcDpy8=
155
+
go.hacdias.com/indielib v0.3.1/go.mod h1:ushJ07W6LxAbZWhyqXzQQxXkalPkZo6cGz5Uj2wOdb4=
156
+
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
157
+
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
158
+
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
159
+
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
160
+
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
161
+
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
162
+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
163
+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
164
+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
165
+
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
166
+
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
167
+
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
168
+
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
169
+
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
170
+
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
171
+
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
172
+
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
173
+
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
174
+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
175
+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
176
+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
177
+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
178
+
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
179
+
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
180
+
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
181
+
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
182
+
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
183
+
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
184
+
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
185
+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
186
+
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
187
+
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
188
+
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
189
+
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
190
+
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
191
+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
192
+
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
193
+
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
194
+
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
195
+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
196
+
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
197
+
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
198
+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
199
+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
200
+
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
201
+
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
202
+
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
203
+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
204
+
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
205
+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
206
+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
207
+
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
208
+
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
209
+
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
210
+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
211
+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
212
+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
213
+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
214
+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
215
+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
216
+
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
217
+
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
218
+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
219
+
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
220
+
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
221
+
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
222
+
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
223
+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
224
+
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
225
+
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
226
+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
227
+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
228
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
229
+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
230
+
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
231
+
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
232
+
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
233
+
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
234
+
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
235
+
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
236
+
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
237
+
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
238
+
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
239
+
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
240
+
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
241
+
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
242
+
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
243
+
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
244
+
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
245
+
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
246
+
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
247
+
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
248
+
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
249
+
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
250
+
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
251
+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
252
+
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
253
+
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
254
+
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
255
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
256
+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
257
+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
258
+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
259
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
260
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
261
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
262
+
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
263
+
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
264
+
willnorris.com/go/microformats v1.2.1-0.20240301064101-b5d1b9d2120e h1:TRIOwo0NxN4KVSgYlYmiQktd9I96YgZ3942/JVzhwTM=
265
+
willnorris.com/go/microformats v1.2.1-0.20240301064101-b5d1b9d2120e/go.mod h1:zzo0hFA/E/nl1ZAjXiXA7KCKwCTdgBU+7HXltGgHeGA=
266
+
willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0 h1:V5+O+YZHchEwu6ZmPcqT1dQ+mHgE356Q+w9SVOQ+QZg=
267
+
willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0/go.mod h1:DgeruqKIsZtcDXVXNbBHa0YYEm88oAnK7PahkDtuCvw=
+241
indieauth.go
+241
indieauth.go
···
1
+
package main
2
+
3
+
import (
4
+
"context"
5
+
"crypto/sha256"
6
+
"crypto/subtle"
7
+
"errors"
8
+
"net/http"
9
+
"net/url"
10
+
"os"
11
+
"strings"
12
+
"time"
13
+
14
+
"github.com/puregarlic/space/pages"
15
+
16
+
"github.com/a-h/templ"
17
+
"github.com/aidarkhanov/nanoid"
18
+
"github.com/golang-jwt/jwt/v5"
19
+
"go.hacdias.com/indielib/indieauth"
20
+
)
21
+
22
+
// storeAuthorization stores the authorization request and returns a code for it.
23
+
// Something such as JWT tokens could be used in a production environment.
24
+
func (s *server) storeAuthorization(req *indieauth.AuthenticationRequest) string {
25
+
code := nanoid.New()
26
+
27
+
s.db.Authorization.Set(code, req, 0)
28
+
29
+
return code
30
+
}
31
+
32
+
type CustomTokenClaims struct {
33
+
Scopes []string `json:"scopes"`
34
+
jwt.RegisteredClaims
35
+
}
36
+
37
+
type contextKey string
38
+
39
+
const (
40
+
scopesContextKey contextKey = "scopes"
41
+
)
42
+
43
+
// authorizationGetHandler handles the GET method for the authorization endpoint.
44
+
func (s *server) authorizationGetHandler(w http.ResponseWriter, r *http.Request) {
45
+
// In a production server, this page would usually be protected. In order for
46
+
// the user to authorize this request, they must be authenticated. This could
47
+
// be done in different ways: username/password, passkeys, etc.
48
+
49
+
// Parse the authorization request.
50
+
req, err := s.ias.ParseAuthorization(r)
51
+
if err != nil {
52
+
serveErrorJSON(w, http.StatusBadRequest, "invalid_request", err.Error())
53
+
return
54
+
}
55
+
56
+
// Do a best effort attempt at fetching more information about the application
57
+
// that we can show to the user. Not all applications provide this sort of
58
+
// information.
59
+
app, _ := s.ias.DiscoverApplicationMetadata(r.Context(), req.ClientID)
60
+
61
+
// Here, we just display a small HTML document where the user has to press
62
+
// to authorize this request. Please note that this template contains a form
63
+
// where we dump all the request information. This makes it possible to reuse
64
+
// [indieauth.Server.ParseAuthorization] when the user authorizes the request.
65
+
templ.Handler(pages.Auth(req, app)).ServeHTTP(w, r)
66
+
}
67
+
68
+
// authorizationPostHandler handles the POST method for the authorization endpoint.
69
+
func (s *server) authorizationPostHandler(w http.ResponseWriter, r *http.Request) {
70
+
s.authorizationCodeExchange(w, r, false)
71
+
}
72
+
73
+
// tokenHandler handles the token endpoint. In our case, we only accept the default
74
+
// type which is exchanging an authorization code for a token.
75
+
func (s *server) tokenHandler(w http.ResponseWriter, r *http.Request) {
76
+
if r.Method != http.MethodPost {
77
+
httpError(w, http.StatusMethodNotAllowed)
78
+
return
79
+
}
80
+
81
+
if r.Form.Get("grant_type") == "refresh_token" {
82
+
// NOTE: this server does not implement refresh tokens.
83
+
// https://indieauth.spec.indieweb.org/#refresh-tokens
84
+
w.WriteHeader(http.StatusNotImplemented)
85
+
return
86
+
}
87
+
88
+
s.authorizationCodeExchange(w, r, true)
89
+
}
90
+
91
+
type tokenResponse struct {
92
+
Me string `json:"me"`
93
+
AccessToken string `json:"access_token,omitempty"`
94
+
TokenType string `json:"token_type,omitempty"`
95
+
Scope string `json:"scope,omitempty"`
96
+
ExpiresIn int64 `json:"expires_in,omitempty"`
97
+
}
98
+
99
+
// authorizationCodeExchange handles the authorization code exchange. It is used by
100
+
// both the authorization handler to exchange the code for the user's profile URL,
101
+
// and by the token endpoint, to exchange the code by a token.
102
+
func (s *server) authorizationCodeExchange(w http.ResponseWriter, r *http.Request, withToken bool) {
103
+
if err := r.ParseForm(); err != nil {
104
+
serveErrorJSON(w, http.StatusBadRequest, "invalid_request", err.Error())
105
+
return
106
+
}
107
+
108
+
// t := s.getAuthorization(r.Form.Get("code"))
109
+
req, present := s.db.Authorization.GetAndDelete(r.Form.Get("code"))
110
+
if !present {
111
+
serveErrorJSON(w, http.StatusBadRequest, "invalid_request", "invalid authorization")
112
+
return
113
+
}
114
+
authRequest := req.Value()
115
+
116
+
err := s.ias.ValidateTokenExchange(authRequest, r)
117
+
if err != nil {
118
+
serveErrorJSON(w, http.StatusBadRequest, "invalid_request", err.Error())
119
+
return
120
+
}
121
+
122
+
response := &tokenResponse{
123
+
Me: s.profileURL,
124
+
}
125
+
126
+
scopes := authRequest.Scopes
127
+
128
+
if withToken {
129
+
now := time.Now()
130
+
expiresAt := now.Add(15 * time.Minute)
131
+
claims := CustomTokenClaims{
132
+
scopes,
133
+
jwt.RegisteredClaims{
134
+
ExpiresAt: jwt.NewNumericDate(expiresAt),
135
+
IssuedAt: jwt.NewNumericDate(now),
136
+
NotBefore: jwt.NewNumericDate(now),
137
+
},
138
+
}
139
+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
140
+
141
+
secret := os.Getenv("JWT_SECRET")
142
+
jwt, err := token.SignedString([]byte(secret))
143
+
if err != nil {
144
+
panic(err)
145
+
}
146
+
147
+
response.AccessToken = jwt
148
+
response.TokenType = "Bearer"
149
+
response.ExpiresIn = int64(time.Until(expiresAt).Seconds())
150
+
response.Scope = strings.Join(scopes, " ")
151
+
}
152
+
153
+
// An actual server may want to include the "profile" in the response if the
154
+
// scope "profile" is included.
155
+
serveJSON(w, http.StatusOK, response)
156
+
}
157
+
158
+
func (s *server) authorizationAcceptHandler(w http.ResponseWriter, r *http.Request) {
159
+
// Parse authorization information. This only works because our authorization page
160
+
// includes all the required information. This can be done in other ways: database,
161
+
// whether temporary or not, cookies, etc.
162
+
req, err := s.ias.ParseAuthorization(r)
163
+
if err != nil {
164
+
serveErrorJSON(w, http.StatusBadRequest, "invalid_request", err.Error())
165
+
return
166
+
}
167
+
168
+
// Generate a random code and persist the information associated to that code.
169
+
// You could do this in other ways: database, or JWT tokens, or both, for example.
170
+
code := s.storeAuthorization(req)
171
+
172
+
// Redirect to client callback.
173
+
query := url.Values{}
174
+
query.Set("code", code)
175
+
query.Set("state", req.State)
176
+
http.Redirect(w, r, req.RedirectURI+"?"+query.Encode(), http.StatusFound)
177
+
}
178
+
179
+
// mustAuth is a middleware to ensure that the request is authorized. The way this
180
+
// works depends on the implementation. It then stores the scopes in the context.
181
+
func (s *server) mustAuth(next http.Handler) http.Handler {
182
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
183
+
tokenStr := r.Header.Get("Authorization")
184
+
tokenStr = strings.TrimPrefix(tokenStr, "Bearer")
185
+
tokenStr = strings.TrimSpace(tokenStr)
186
+
187
+
if len(tokenStr) <= 0 {
188
+
serveErrorJSON(w, http.StatusUnauthorized, "invalid_request", "no credentials")
189
+
return
190
+
}
191
+
192
+
token, err := jwt.ParseWithClaims(tokenStr, &CustomTokenClaims{}, func(t *jwt.Token) (interface{}, error) {
193
+
return []byte(os.Getenv("JWT_SECRET")), nil
194
+
})
195
+
196
+
if err != nil {
197
+
serveErrorJSON(w, http.StatusUnauthorized, "invalid_request", "invalid token")
198
+
return
199
+
} else if claims, ok := token.Claims.(*CustomTokenClaims); ok {
200
+
ctx := context.WithValue(r.Context(), scopesContextKey, claims.Scopes)
201
+
next.ServeHTTP(w, r.WithContext(ctx))
202
+
return
203
+
} else {
204
+
serveErrorJSON(w, http.StatusUnauthorized, "invalid_request", "malformed claims")
205
+
return
206
+
}
207
+
})
208
+
}
209
+
210
+
func (s *server) mustBasicAuth(next http.Handler) http.Handler {
211
+
user, ok := os.LookupEnv("ADMIN_USERNAME")
212
+
if !ok {
213
+
panic(errors.New("ADMIN_USERNAME is not set, cannot start"))
214
+
}
215
+
216
+
pass, ok := os.LookupEnv("ADMIN_PASSWORD")
217
+
if !ok {
218
+
panic(errors.New("ADMIN_PASSWORD is not set, cannot start"))
219
+
}
220
+
221
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
222
+
username, password, ok := r.BasicAuth()
223
+
if ok {
224
+
usernameHash := sha256.Sum256([]byte(username))
225
+
passwordHash := sha256.Sum256([]byte(password))
226
+
expectedUsernameHash := sha256.Sum256([]byte(user))
227
+
expectedPasswordHash := sha256.Sum256([]byte(pass))
228
+
229
+
usernameMatch := (subtle.ConstantTimeCompare(usernameHash[:], expectedUsernameHash[:]) == 1)
230
+
passwordMatch := (subtle.ConstantTimeCompare(passwordHash[:], expectedPasswordHash[:]) == 1)
231
+
232
+
if usernameMatch && passwordMatch {
233
+
next.ServeHTTP(w, r)
234
+
return
235
+
}
236
+
}
237
+
238
+
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
239
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
240
+
})
241
+
}
+186
main.go
+186
main.go
···
1
+
package main
2
+
3
+
import (
4
+
"encoding/json"
5
+
"flag"
6
+
"time"
7
+
8
+
"log"
9
+
"net/http"
10
+
"strconv"
11
+
12
+
"github.com/ostafen/clover/v2/query"
13
+
14
+
"github.com/puregarlic/space/db"
15
+
"github.com/puregarlic/space/pages"
16
+
"github.com/puregarlic/space/types"
17
+
18
+
"github.com/a-h/templ"
19
+
"github.com/go-chi/chi/v5"
20
+
"github.com/go-chi/chi/v5/middleware"
21
+
"github.com/go-chi/httprate"
22
+
23
+
"go.hacdias.com/indielib/indieauth"
24
+
"go.hacdias.com/indielib/microformats"
25
+
"go.hacdias.com/indielib/micropub"
26
+
27
+
_ "github.com/joho/godotenv/autoload"
28
+
)
29
+
30
+
func main() {
31
+
// Setup flags.
32
+
portPtr := flag.Int("port", 80, "port to listen on")
33
+
addressPtr := flag.String("profile", "http://localhost/", "client URL and front facing address to listen on")
34
+
flag.Parse()
35
+
36
+
profileURL := *addressPtr
37
+
38
+
// Validate the given Client ID before starting the HTTP server.
39
+
err := indieauth.IsValidProfileURL(profileURL)
40
+
if err != nil {
41
+
log.Fatal(err)
42
+
}
43
+
44
+
// Setup storage handlers
45
+
store := db.NewStorage()
46
+
defer store.Cleanup()
47
+
48
+
// Create a new client.
49
+
s := &server{
50
+
profileURL: profileURL,
51
+
ias: indieauth.NewServer(true, nil),
52
+
db: store,
53
+
}
54
+
55
+
r := chi.NewRouter()
56
+
57
+
r.Use(middleware.RequestID)
58
+
r.Use(middleware.RealIP)
59
+
r.Use(middleware.Logger)
60
+
r.Use(middleware.Recoverer)
61
+
62
+
r.Use(httprate.LimitByIP(100, 1*time.Minute))
63
+
64
+
// Static resources
65
+
r.Get("/static/*", http.StripPrefix("/static", http.FileServer(http.Dir("static"))).ServeHTTP)
66
+
67
+
// Pages
68
+
r.Get("/", s.serveHomeTemplate)
69
+
r.Get("/posts/{slug}", s.servePostTemplate)
70
+
71
+
// IndieAuth handlers
72
+
r.Group(func(r chi.Router) {
73
+
r.Post("/token", s.tokenHandler)
74
+
r.Post("/authorization", s.authorizationPostHandler)
75
+
r.Post("/authorization/accept", s.authorizationAcceptHandler)
76
+
77
+
// User authentication portal
78
+
r.With(s.mustBasicAuth).Get("/authorization", s.authorizationGetHandler)
79
+
})
80
+
81
+
// Micropub handler
82
+
r.Route("/micropub", func(r chi.Router) {
83
+
r.Use(s.mustAuth)
84
+
r.Get("/", s.serveMicropub)
85
+
r.Post("/", s.serveMicropub)
86
+
})
87
+
88
+
// Start it!
89
+
log.Printf("Listening on http://localhost:%d", *portPtr)
90
+
log.Printf("Listening on %s", profileURL)
91
+
if err := http.ListenAndServe(":"+strconv.Itoa(*portPtr), r); err != nil {
92
+
log.Fatal(err)
93
+
}
94
+
}
95
+
96
+
type server struct {
97
+
profileURL string
98
+
ias *indieauth.Server
99
+
db *db.Storage
100
+
}
101
+
102
+
func (s *server) serveHomeTemplate(w http.ResponseWriter, r *http.Request) {
103
+
q := query.NewQuery(
104
+
string(db.PostCollection),
105
+
).Sort(query.SortOption{
106
+
Field: "createdAt",
107
+
Direction: -1,
108
+
}).Limit(10)
109
+
110
+
docs, err := s.db.Docs.FindAll(q)
111
+
if err != nil {
112
+
httpError(w, http.StatusInternalServerError)
113
+
panic(err)
114
+
}
115
+
116
+
posts := make([]*types.Post, len(docs))
117
+
for i, doc := range docs {
118
+
id := doc.ObjectId()
119
+
post := &types.Post{
120
+
ID: id,
121
+
}
122
+
123
+
if err := doc.Unmarshal(post); err != nil {
124
+
httpError(w, http.StatusInternalServerError)
125
+
panic(err)
126
+
}
127
+
128
+
post.ID = id
129
+
130
+
posts[i] = post
131
+
}
132
+
133
+
templ.Handler(pages.Home(s.profileURL, posts)).ServeHTTP(w, r)
134
+
}
135
+
136
+
func (s *server) servePostTemplate(w http.ResponseWriter, r *http.Request) {
137
+
id := chi.URLParam(r, "slug")
138
+
post := &types.Post{}
139
+
140
+
doc, err := s.db.Docs.FindById(string(db.PostCollection), id)
141
+
if err != nil {
142
+
httpError(w, http.StatusInternalServerError)
143
+
return
144
+
} else if doc == nil {
145
+
httpError(w, http.StatusNotFound)
146
+
return
147
+
}
148
+
149
+
if err := doc.Unmarshal(post); err != nil {
150
+
httpError(w, http.StatusInternalServerError)
151
+
return
152
+
}
153
+
154
+
templ.Handler(pages.Post(post)).ServeHTTP(w, r)
155
+
}
156
+
157
+
func (s *server) serveMicropub(w http.ResponseWriter, r *http.Request) {
158
+
micropub.NewHandler(
159
+
µpubImplementation{s},
160
+
micropub.WithGetPostTypes(func() []micropub.PostType {
161
+
return []micropub.PostType{
162
+
{
163
+
Name: "Post",
164
+
Type: string(microformats.TypeNote),
165
+
},
166
+
}
167
+
}),
168
+
).ServeHTTP(w, r)
169
+
}
170
+
171
+
func httpError(w http.ResponseWriter, status int) {
172
+
http.Error(w, http.StatusText(status), status)
173
+
}
174
+
175
+
func serveJSON(w http.ResponseWriter, code int, data interface{}) {
176
+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
177
+
w.WriteHeader(code)
178
+
_ = json.NewEncoder(w).Encode(data)
179
+
}
180
+
181
+
func serveErrorJSON(w http.ResponseWriter, code int, err, errDescription string) {
182
+
serveJSON(w, code, map[string]string{
183
+
"error": err,
184
+
"error_description": errDescription,
185
+
})
186
+
}
+212
micropub.go
+212
micropub.go
···
1
+
package main
2
+
3
+
import (
4
+
"errors"
5
+
"fmt"
6
+
"net/http"
7
+
urlpkg "net/url"
8
+
"reflect"
9
+
"strings"
10
+
"time"
11
+
12
+
"github.com/ostafen/clover/v2/document"
13
+
"github.com/puregarlic/space/db"
14
+
"github.com/puregarlic/space/types"
15
+
"github.com/samber/lo"
16
+
17
+
"go.hacdias.com/indielib/micropub"
18
+
)
19
+
20
+
type micropubImplementation struct {
21
+
*server
22
+
}
23
+
24
+
func postIdFromUrlPath(path string) string {
25
+
return strings.TrimPrefix(path, "/posts/")
26
+
}
27
+
28
+
func (s *micropubImplementation) HasScope(r *http.Request, scope string) bool {
29
+
v := r.Context().Value(scopesContextKey)
30
+
if scopes, ok := v.([]string); ok {
31
+
for _, sc := range scopes {
32
+
if sc == scope {
33
+
return true
34
+
}
35
+
}
36
+
}
37
+
38
+
return false
39
+
}
40
+
41
+
func (s *micropubImplementation) Source(urlStr string) (map[string]any, error) {
42
+
url, err := urlpkg.Parse(urlStr)
43
+
if err != nil {
44
+
return nil, fmt.Errorf("%w: %w", micropub.ErrBadRequest, err)
45
+
}
46
+
47
+
id := postIdFromUrlPath(url.Path)
48
+
post := &types.Post{}
49
+
doc, err := s.server.db.Docs.FindById(string(db.PostCollection), id)
50
+
if err != nil {
51
+
panic(err)
52
+
} else if doc == nil {
53
+
return nil, micropub.ErrNotFound
54
+
}
55
+
56
+
if err := doc.Unmarshal(post); err != nil {
57
+
panic(err)
58
+
}
59
+
60
+
return map[string]any{
61
+
"type": []string{post.Type},
62
+
"properties": post.Properties,
63
+
}, nil
64
+
}
65
+
66
+
func (s *micropubImplementation) SourceMany(limit, offset int) ([]map[string]any, error) {
67
+
return nil, micropub.ErrNotImplemented
68
+
}
69
+
70
+
func (s *micropubImplementation) Create(req *micropub.Request) (string, error) {
71
+
post := types.Post{
72
+
Type: req.Type,
73
+
Properties: req.Properties,
74
+
CreatedAt: time.Now().Unix(),
75
+
}
76
+
doc := document.NewDocumentOf(post)
77
+
if doc == nil {
78
+
return "", errors.New("Could not marshal post to Clover document")
79
+
}
80
+
81
+
id, err := s.server.db.Docs.InsertOne(string(db.PostCollection), doc)
82
+
if err != nil {
83
+
return "", err
84
+
}
85
+
86
+
return s.profileURL + "posts/" + id, nil
87
+
}
88
+
89
+
func (s *micropubImplementation) Update(req *micropub.Request) (string, error) {
90
+
url, err := urlpkg.Parse(req.URL)
91
+
if err != nil {
92
+
return "", fmt.Errorf("%w: %w", micropub.ErrBadRequest, err)
93
+
}
94
+
95
+
id := postIdFromUrlPath(url.Path)
96
+
97
+
if err := s.server.db.Docs.UpdateById(
98
+
string(db.PostCollection),
99
+
id,
100
+
func(doc *document.Document) *document.Document {
101
+
post := &types.Post{}
102
+
if err := doc.Unmarshal(post); err != nil {
103
+
panic(err)
104
+
}
105
+
106
+
props, err := updateProperties(post.Properties, req)
107
+
if err != nil {
108
+
panic(err)
109
+
}
110
+
111
+
doc.Set("properties", props)
112
+
113
+
return doc
114
+
},
115
+
); err != nil {
116
+
return "", fmt.Errorf("%w: %w", micropub.ErrBadRequest, err)
117
+
}
118
+
119
+
return s.profileURL + url.Path, nil
120
+
}
121
+
122
+
func (s *micropubImplementation) Delete(urlStr string) error {
123
+
url, err := urlpkg.Parse(urlStr)
124
+
if err != nil {
125
+
return fmt.Errorf("%w: %w", micropub.ErrBadRequest, err)
126
+
}
127
+
128
+
id := postIdFromUrlPath(url.Path)
129
+
130
+
if err := s.server.db.Docs.DeleteById(string(db.PostCollection), id); err != nil {
131
+
return fmt.Errorf("%w: %w", micropub.ErrBadRequest, err)
132
+
}
133
+
134
+
return nil
135
+
}
136
+
137
+
func (s *micropubImplementation) Undelete(url string) error {
138
+
return micropub.ErrNotImplemented
139
+
}
140
+
141
+
// updateProperties applies the updates (additions, deletions, replacements)
142
+
// in the given [micropub.Request] to a set of existing microformats properties.
143
+
func updateProperties(properties map[string][]any, req *micropub.Request) (map[string][]any, error) {
144
+
if req.Updates.Replace != nil {
145
+
for key, value := range req.Updates.Replace {
146
+
properties[key] = value
147
+
}
148
+
}
149
+
150
+
if req.Updates.Add != nil {
151
+
for key, value := range req.Updates.Add {
152
+
switch key {
153
+
case "name":
154
+
return nil, errors.New("cannot add a new name")
155
+
case "content":
156
+
return nil, errors.New("cannot add content")
157
+
default:
158
+
if key == "published" {
159
+
if _, ok := properties["published"]; ok {
160
+
return nil, errors.New("cannot replace published through add method")
161
+
}
162
+
}
163
+
164
+
if _, ok := properties[key]; !ok {
165
+
properties[key] = []any{}
166
+
}
167
+
168
+
properties[key] = append(properties[key], value...)
169
+
}
170
+
}
171
+
}
172
+
173
+
if req.Updates.Delete != nil {
174
+
if reflect.TypeOf(req.Updates.Delete).Kind() == reflect.Slice {
175
+
toDelete, ok := req.Updates.Delete.([]any)
176
+
if !ok {
177
+
return nil, errors.New("invalid delete array")
178
+
}
179
+
180
+
for _, key := range toDelete {
181
+
delete(properties, fmt.Sprint(key))
182
+
}
183
+
} else {
184
+
toDelete, ok := req.Updates.Delete.(map[string]any)
185
+
if !ok {
186
+
return nil, fmt.Errorf("invalid delete object: expected map[string]any, got: %s", reflect.TypeOf(req.Updates.Delete))
187
+
}
188
+
189
+
for key, v := range toDelete {
190
+
value, ok := v.([]any)
191
+
if !ok {
192
+
return nil, fmt.Errorf("invalid value: expected []any, got: %s", reflect.TypeOf(value))
193
+
}
194
+
195
+
if _, ok := properties[key]; !ok {
196
+
properties[key] = []any{}
197
+
}
198
+
199
+
properties[key] = lo.Filter(properties[key], func(ss any, _ int) bool {
200
+
for _, s := range value {
201
+
if s == ss {
202
+
return false
203
+
}
204
+
}
205
+
return true
206
+
})
207
+
}
208
+
}
209
+
}
210
+
211
+
return properties, nil
212
+
}
+15
package.json
+15
package.json
···
1
+
{
2
+
"name": "puregarlicspace",
3
+
"version": "1.0.0",
4
+
"description": "",
5
+
"main": "index.js",
6
+
"scripts": {
7
+
"test": "echo \"Error: no test specified\" && exit 1"
8
+
},
9
+
"keywords": [],
10
+
"author": "",
11
+
"license": "ISC",
12
+
"devDependencies": {
13
+
"tailwindcss": "^3.4.7"
14
+
}
15
+
}
+56
pages/auth.templ
+56
pages/auth.templ
···
1
+
package pages
2
+
3
+
import "strings"
4
+
5
+
import "go.hacdias.com/indielib/indieauth"
6
+
7
+
templ Auth( req *indieauth.AuthenticationRequest, app *indieauth.ApplicationMetadata ) {
8
+
<!DOCTYPE html>
9
+
<html>
10
+
<head>
11
+
<title>Authorization | Micropub and IndieAuth Server Demo</title>
12
+
</head>
13
+
<body>
14
+
<h1>IndieAuth Server Demo: Authorization</h1>
15
+
16
+
<p>
17
+
You received an authorization request from
18
+
19
+
if app != nil {
20
+
if len(app.Logo) > 0 {
21
+
<img style="width: 1em; vertical-align: middle" src={ app.Logo } />
22
+
}
23
+
24
+
<strong>{ app.Name }</strong> by { app.Author }:
25
+
} else {
26
+
the following client:
27
+
}
28
+
</p>
29
+
30
+
<ul>
31
+
<li><strong>Redirect:</strong> <code>{ req.ClientID }</code></li>
32
+
<li><strong>Client:</strong> <code>{ req.RedirectURI }</code></li>
33
+
</ul>
34
+
35
+
<p>For the following scopes:
36
+
for _, scope := range req.Scopes {
37
+
<code>{ scope }</code>
38
+
}
39
+
.</p>
40
+
41
+
<form method='post' action='/authorization/accept'>
42
+
<input type="hidden" name="response_type" value="code">
43
+
<input type="hidden" name="scope" value={ strings.Join(req.Scopes, " ") }>
44
+
<input type="hidden" name="redirect_uri" value={ req.RedirectURI }>
45
+
<input type="hidden" name="client_id" value={ req.ClientID }>
46
+
<input type="hidden" name="state" value={ req.State }>
47
+
<input type="hidden" name="code_challenge" value={ req.CodeChallenge }>
48
+
<input type="hidden" name="code_challenge_method" value={ req.CodeChallengeMethod }>
49
+
50
+
<p>In a production server, this page could be behind some sort of authentication mechanism, such as username and password, PassKey, etc.</p>
51
+
52
+
<button id="submit">Authorize</button>
53
+
</form>
54
+
</body>
55
+
</html>
56
+
}
+32
pages/home.templ
+32
pages/home.templ
···
1
+
package pages
2
+
3
+
import "github.com/puregarlic/space/types"
4
+
import "fmt"
5
+
6
+
templ Home(profileUrl string, posts []*types.Post) {
7
+
<!DOCTYPE html>
8
+
<html>
9
+
<head>
10
+
<title>Micropub and IndieAuth Server Demo</title>
11
+
<link rel="authorization_endpoint" href="/authorization">
12
+
<link rel="token_endpoint" href="/token">
13
+
<link rel="micropub" href="/micropub">
14
+
15
+
<link rel="stylesheet" href="/static/styles.css" />
16
+
</head>
17
+
<body>
18
+
<h1>Micropub and IndieAuth Server Demo</h1>
19
+
20
+
<p>Sign in on a website that supports IndieAuth. Use <code>{ profileUrl }</code> as your domain.</p>
21
+
22
+
<h2>Posts</h2>
23
+
24
+
<p>You can create posts using a Micropub client.</p>
25
+
<ul>
26
+
for _, post := range posts {
27
+
<li><a href={ templ.URL("/posts/" + post.ID) }>{ post.ID }</a> - { fmt.Sprint(post.Properties["content"]) }</li>
28
+
}
29
+
</ul>
30
+
</body>
31
+
</html>
32
+
}
+56
pages/post.templ
+56
pages/post.templ
···
1
+
package pages
2
+
3
+
import "github.com/puregarlic/space/types"
4
+
import "fmt"
5
+
import "encoding/json"
6
+
import "reflect"
7
+
8
+
func printProperty(post *types.Post, name string) string {
9
+
if val, ok := post.Properties[name]; ok {
10
+
tp := reflect.TypeOf(val)
11
+
switch tp.Kind() {
12
+
default:
13
+
return fmt.Sprint(val)
14
+
case reflect.Slice:
15
+
str := ""
16
+
for _, v := range val {
17
+
str = str + fmt.Sprint(v)
18
+
}
19
+
20
+
return str
21
+
}
22
+
23
+
}
24
+
25
+
return "<no name provided>"
26
+
}
27
+
28
+
func printPost(post *types.Post) string {
29
+
out, err := json.Marshal(post)
30
+
31
+
if (err != nil) {
32
+
panic (err)
33
+
}
34
+
35
+
return fmt.Sprint(string(out))
36
+
}
37
+
38
+
templ Post(post *types.Post) {
39
+
<!DOCTYPE html>
40
+
<html>
41
+
<head>
42
+
<title>Post | Micropub and IndieAuth Server Demo</title>
43
+
</head>
44
+
<body>
45
+
<div class={ post.Type }>
46
+
<h1 class="p-name">{ printProperty(post, "name") }</h1>
47
+
<p class="p-content">{ printProperty(post, "content") }</p>
48
+
49
+
<h3>Stored Microformats</h3>
50
+
<code>
51
+
<pre>{ printPost(post) }</pre>
52
+
</code>
53
+
</div>
54
+
</body>
55
+
</html>
56
+
}
+842
pnpm-lock.yaml
+842
pnpm-lock.yaml
···
1
+
lockfileVersion: '9.0'
2
+
3
+
settings:
4
+
autoInstallPeers: true
5
+
excludeLinksFromLockfile: false
6
+
7
+
importers:
8
+
9
+
.:
10
+
devDependencies:
11
+
tailwindcss:
12
+
specifier: ^3.4.7
13
+
version: 3.4.7
14
+
15
+
packages:
16
+
17
+
'@alloc/quick-lru@5.2.0':
18
+
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
19
+
engines: {node: '>=10'}
20
+
21
+
'@isaacs/cliui@8.0.2':
22
+
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
23
+
engines: {node: '>=12'}
24
+
25
+
'@jridgewell/gen-mapping@0.3.5':
26
+
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
27
+
engines: {node: '>=6.0.0'}
28
+
29
+
'@jridgewell/resolve-uri@3.1.2':
30
+
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
31
+
engines: {node: '>=6.0.0'}
32
+
33
+
'@jridgewell/set-array@1.2.1':
34
+
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
35
+
engines: {node: '>=6.0.0'}
36
+
37
+
'@jridgewell/sourcemap-codec@1.5.0':
38
+
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
39
+
40
+
'@jridgewell/trace-mapping@0.3.25':
41
+
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
42
+
43
+
'@nodelib/fs.scandir@2.1.5':
44
+
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
45
+
engines: {node: '>= 8'}
46
+
47
+
'@nodelib/fs.stat@2.0.5':
48
+
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
49
+
engines: {node: '>= 8'}
50
+
51
+
'@nodelib/fs.walk@1.2.8':
52
+
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
53
+
engines: {node: '>= 8'}
54
+
55
+
'@pkgjs/parseargs@0.11.0':
56
+
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
57
+
engines: {node: '>=14'}
58
+
59
+
ansi-regex@5.0.1:
60
+
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
61
+
engines: {node: '>=8'}
62
+
63
+
ansi-regex@6.0.1:
64
+
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
65
+
engines: {node: '>=12'}
66
+
67
+
ansi-styles@4.3.0:
68
+
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
69
+
engines: {node: '>=8'}
70
+
71
+
ansi-styles@6.2.1:
72
+
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
73
+
engines: {node: '>=12'}
74
+
75
+
any-promise@1.3.0:
76
+
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
77
+
78
+
anymatch@3.1.3:
79
+
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
80
+
engines: {node: '>= 8'}
81
+
82
+
arg@5.0.2:
83
+
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
84
+
85
+
balanced-match@1.0.2:
86
+
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
87
+
88
+
binary-extensions@2.3.0:
89
+
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
90
+
engines: {node: '>=8'}
91
+
92
+
brace-expansion@2.0.1:
93
+
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
94
+
95
+
braces@3.0.3:
96
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
97
+
engines: {node: '>=8'}
98
+
99
+
camelcase-css@2.0.1:
100
+
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
101
+
engines: {node: '>= 6'}
102
+
103
+
chokidar@3.6.0:
104
+
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
105
+
engines: {node: '>= 8.10.0'}
106
+
107
+
color-convert@2.0.1:
108
+
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
109
+
engines: {node: '>=7.0.0'}
110
+
111
+
color-name@1.1.4:
112
+
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
113
+
114
+
commander@4.1.1:
115
+
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
116
+
engines: {node: '>= 6'}
117
+
118
+
cross-spawn@7.0.3:
119
+
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
120
+
engines: {node: '>= 8'}
121
+
122
+
cssesc@3.0.0:
123
+
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
124
+
engines: {node: '>=4'}
125
+
hasBin: true
126
+
127
+
didyoumean@1.2.2:
128
+
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
129
+
130
+
dlv@1.1.3:
131
+
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
132
+
133
+
eastasianwidth@0.2.0:
134
+
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
135
+
136
+
emoji-regex@8.0.0:
137
+
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
138
+
139
+
emoji-regex@9.2.2:
140
+
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
141
+
142
+
fast-glob@3.3.2:
143
+
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
144
+
engines: {node: '>=8.6.0'}
145
+
146
+
fastq@1.17.1:
147
+
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
148
+
149
+
fill-range@7.1.1:
150
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
151
+
engines: {node: '>=8'}
152
+
153
+
foreground-child@3.2.1:
154
+
resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==}
155
+
engines: {node: '>=14'}
156
+
157
+
fsevents@2.3.3:
158
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
159
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
160
+
os: [darwin]
161
+
162
+
function-bind@1.1.2:
163
+
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
164
+
165
+
glob-parent@5.1.2:
166
+
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
167
+
engines: {node: '>= 6'}
168
+
169
+
glob-parent@6.0.2:
170
+
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
171
+
engines: {node: '>=10.13.0'}
172
+
173
+
glob@10.4.5:
174
+
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
175
+
hasBin: true
176
+
177
+
hasown@2.0.2:
178
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
179
+
engines: {node: '>= 0.4'}
180
+
181
+
is-binary-path@2.1.0:
182
+
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
183
+
engines: {node: '>=8'}
184
+
185
+
is-core-module@2.15.0:
186
+
resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==}
187
+
engines: {node: '>= 0.4'}
188
+
189
+
is-extglob@2.1.1:
190
+
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
191
+
engines: {node: '>=0.10.0'}
192
+
193
+
is-fullwidth-code-point@3.0.0:
194
+
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
195
+
engines: {node: '>=8'}
196
+
197
+
is-glob@4.0.3:
198
+
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
199
+
engines: {node: '>=0.10.0'}
200
+
201
+
is-number@7.0.0:
202
+
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
203
+
engines: {node: '>=0.12.0'}
204
+
205
+
isexe@2.0.0:
206
+
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
207
+
208
+
jackspeak@3.4.3:
209
+
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
210
+
211
+
jiti@1.21.6:
212
+
resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
213
+
hasBin: true
214
+
215
+
lilconfig@2.1.0:
216
+
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
217
+
engines: {node: '>=10'}
218
+
219
+
lilconfig@3.1.2:
220
+
resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
221
+
engines: {node: '>=14'}
222
+
223
+
lines-and-columns@1.2.4:
224
+
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
225
+
226
+
lru-cache@10.4.3:
227
+
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
228
+
229
+
merge2@1.4.1:
230
+
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
231
+
engines: {node: '>= 8'}
232
+
233
+
micromatch@4.0.7:
234
+
resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
235
+
engines: {node: '>=8.6'}
236
+
237
+
minimatch@9.0.5:
238
+
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
239
+
engines: {node: '>=16 || 14 >=14.17'}
240
+
241
+
minipass@7.1.2:
242
+
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
243
+
engines: {node: '>=16 || 14 >=14.17'}
244
+
245
+
mz@2.7.0:
246
+
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
247
+
248
+
nanoid@3.3.7:
249
+
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
250
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
251
+
hasBin: true
252
+
253
+
normalize-path@3.0.0:
254
+
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
255
+
engines: {node: '>=0.10.0'}
256
+
257
+
object-assign@4.1.1:
258
+
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
259
+
engines: {node: '>=0.10.0'}
260
+
261
+
object-hash@3.0.0:
262
+
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
263
+
engines: {node: '>= 6'}
264
+
265
+
package-json-from-dist@1.0.0:
266
+
resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
267
+
268
+
path-key@3.1.1:
269
+
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
270
+
engines: {node: '>=8'}
271
+
272
+
path-parse@1.0.7:
273
+
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
274
+
275
+
path-scurry@1.11.1:
276
+
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
277
+
engines: {node: '>=16 || 14 >=14.18'}
278
+
279
+
picocolors@1.0.1:
280
+
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
281
+
282
+
picomatch@2.3.1:
283
+
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
284
+
engines: {node: '>=8.6'}
285
+
286
+
pify@2.3.0:
287
+
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
288
+
engines: {node: '>=0.10.0'}
289
+
290
+
pirates@4.0.6:
291
+
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
292
+
engines: {node: '>= 6'}
293
+
294
+
postcss-import@15.1.0:
295
+
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
296
+
engines: {node: '>=14.0.0'}
297
+
peerDependencies:
298
+
postcss: ^8.0.0
299
+
300
+
postcss-js@4.0.1:
301
+
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
302
+
engines: {node: ^12 || ^14 || >= 16}
303
+
peerDependencies:
304
+
postcss: ^8.4.21
305
+
306
+
postcss-load-config@4.0.2:
307
+
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
308
+
engines: {node: '>= 14'}
309
+
peerDependencies:
310
+
postcss: '>=8.0.9'
311
+
ts-node: '>=9.0.0'
312
+
peerDependenciesMeta:
313
+
postcss:
314
+
optional: true
315
+
ts-node:
316
+
optional: true
317
+
318
+
postcss-nested@6.2.0:
319
+
resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
320
+
engines: {node: '>=12.0'}
321
+
peerDependencies:
322
+
postcss: ^8.2.14
323
+
324
+
postcss-selector-parser@6.1.1:
325
+
resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==}
326
+
engines: {node: '>=4'}
327
+
328
+
postcss-value-parser@4.2.0:
329
+
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
330
+
331
+
postcss@8.4.40:
332
+
resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==}
333
+
engines: {node: ^10 || ^12 || >=14}
334
+
335
+
queue-microtask@1.2.3:
336
+
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
337
+
338
+
read-cache@1.0.0:
339
+
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
340
+
341
+
readdirp@3.6.0:
342
+
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
343
+
engines: {node: '>=8.10.0'}
344
+
345
+
resolve@1.22.8:
346
+
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
347
+
hasBin: true
348
+
349
+
reusify@1.0.4:
350
+
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
351
+
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
352
+
353
+
run-parallel@1.2.0:
354
+
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
355
+
356
+
shebang-command@2.0.0:
357
+
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
358
+
engines: {node: '>=8'}
359
+
360
+
shebang-regex@3.0.0:
361
+
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
362
+
engines: {node: '>=8'}
363
+
364
+
signal-exit@4.1.0:
365
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
366
+
engines: {node: '>=14'}
367
+
368
+
source-map-js@1.2.0:
369
+
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
370
+
engines: {node: '>=0.10.0'}
371
+
372
+
string-width@4.2.3:
373
+
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
374
+
engines: {node: '>=8'}
375
+
376
+
string-width@5.1.2:
377
+
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
378
+
engines: {node: '>=12'}
379
+
380
+
strip-ansi@6.0.1:
381
+
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
382
+
engines: {node: '>=8'}
383
+
384
+
strip-ansi@7.1.0:
385
+
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
386
+
engines: {node: '>=12'}
387
+
388
+
sucrase@3.35.0:
389
+
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
390
+
engines: {node: '>=16 || 14 >=14.17'}
391
+
hasBin: true
392
+
393
+
supports-preserve-symlinks-flag@1.0.0:
394
+
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
395
+
engines: {node: '>= 0.4'}
396
+
397
+
tailwindcss@3.4.7:
398
+
resolution: {integrity: sha512-rxWZbe87YJb4OcSopb7up2Ba4U82BoiSGUdoDr3Ydrg9ckxFS/YWsvhN323GMcddgU65QRy7JndC7ahhInhvlQ==}
399
+
engines: {node: '>=14.0.0'}
400
+
hasBin: true
401
+
402
+
thenify-all@1.6.0:
403
+
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
404
+
engines: {node: '>=0.8'}
405
+
406
+
thenify@3.3.1:
407
+
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
408
+
409
+
to-regex-range@5.0.1:
410
+
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
411
+
engines: {node: '>=8.0'}
412
+
413
+
ts-interface-checker@0.1.13:
414
+
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
415
+
416
+
util-deprecate@1.0.2:
417
+
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
418
+
419
+
which@2.0.2:
420
+
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
421
+
engines: {node: '>= 8'}
422
+
hasBin: true
423
+
424
+
wrap-ansi@7.0.0:
425
+
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
426
+
engines: {node: '>=10'}
427
+
428
+
wrap-ansi@8.1.0:
429
+
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
430
+
engines: {node: '>=12'}
431
+
432
+
yaml@2.5.0:
433
+
resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==}
434
+
engines: {node: '>= 14'}
435
+
hasBin: true
436
+
437
+
snapshots:
438
+
439
+
'@alloc/quick-lru@5.2.0': {}
440
+
441
+
'@isaacs/cliui@8.0.2':
442
+
dependencies:
443
+
string-width: 5.1.2
444
+
string-width-cjs: string-width@4.2.3
445
+
strip-ansi: 7.1.0
446
+
strip-ansi-cjs: strip-ansi@6.0.1
447
+
wrap-ansi: 8.1.0
448
+
wrap-ansi-cjs: wrap-ansi@7.0.0
449
+
450
+
'@jridgewell/gen-mapping@0.3.5':
451
+
dependencies:
452
+
'@jridgewell/set-array': 1.2.1
453
+
'@jridgewell/sourcemap-codec': 1.5.0
454
+
'@jridgewell/trace-mapping': 0.3.25
455
+
456
+
'@jridgewell/resolve-uri@3.1.2': {}
457
+
458
+
'@jridgewell/set-array@1.2.1': {}
459
+
460
+
'@jridgewell/sourcemap-codec@1.5.0': {}
461
+
462
+
'@jridgewell/trace-mapping@0.3.25':
463
+
dependencies:
464
+
'@jridgewell/resolve-uri': 3.1.2
465
+
'@jridgewell/sourcemap-codec': 1.5.0
466
+
467
+
'@nodelib/fs.scandir@2.1.5':
468
+
dependencies:
469
+
'@nodelib/fs.stat': 2.0.5
470
+
run-parallel: 1.2.0
471
+
472
+
'@nodelib/fs.stat@2.0.5': {}
473
+
474
+
'@nodelib/fs.walk@1.2.8':
475
+
dependencies:
476
+
'@nodelib/fs.scandir': 2.1.5
477
+
fastq: 1.17.1
478
+
479
+
'@pkgjs/parseargs@0.11.0':
480
+
optional: true
481
+
482
+
ansi-regex@5.0.1: {}
483
+
484
+
ansi-regex@6.0.1: {}
485
+
486
+
ansi-styles@4.3.0:
487
+
dependencies:
488
+
color-convert: 2.0.1
489
+
490
+
ansi-styles@6.2.1: {}
491
+
492
+
any-promise@1.3.0: {}
493
+
494
+
anymatch@3.1.3:
495
+
dependencies:
496
+
normalize-path: 3.0.0
497
+
picomatch: 2.3.1
498
+
499
+
arg@5.0.2: {}
500
+
501
+
balanced-match@1.0.2: {}
502
+
503
+
binary-extensions@2.3.0: {}
504
+
505
+
brace-expansion@2.0.1:
506
+
dependencies:
507
+
balanced-match: 1.0.2
508
+
509
+
braces@3.0.3:
510
+
dependencies:
511
+
fill-range: 7.1.1
512
+
513
+
camelcase-css@2.0.1: {}
514
+
515
+
chokidar@3.6.0:
516
+
dependencies:
517
+
anymatch: 3.1.3
518
+
braces: 3.0.3
519
+
glob-parent: 5.1.2
520
+
is-binary-path: 2.1.0
521
+
is-glob: 4.0.3
522
+
normalize-path: 3.0.0
523
+
readdirp: 3.6.0
524
+
optionalDependencies:
525
+
fsevents: 2.3.3
526
+
527
+
color-convert@2.0.1:
528
+
dependencies:
529
+
color-name: 1.1.4
530
+
531
+
color-name@1.1.4: {}
532
+
533
+
commander@4.1.1: {}
534
+
535
+
cross-spawn@7.0.3:
536
+
dependencies:
537
+
path-key: 3.1.1
538
+
shebang-command: 2.0.0
539
+
which: 2.0.2
540
+
541
+
cssesc@3.0.0: {}
542
+
543
+
didyoumean@1.2.2: {}
544
+
545
+
dlv@1.1.3: {}
546
+
547
+
eastasianwidth@0.2.0: {}
548
+
549
+
emoji-regex@8.0.0: {}
550
+
551
+
emoji-regex@9.2.2: {}
552
+
553
+
fast-glob@3.3.2:
554
+
dependencies:
555
+
'@nodelib/fs.stat': 2.0.5
556
+
'@nodelib/fs.walk': 1.2.8
557
+
glob-parent: 5.1.2
558
+
merge2: 1.4.1
559
+
micromatch: 4.0.7
560
+
561
+
fastq@1.17.1:
562
+
dependencies:
563
+
reusify: 1.0.4
564
+
565
+
fill-range@7.1.1:
566
+
dependencies:
567
+
to-regex-range: 5.0.1
568
+
569
+
foreground-child@3.2.1:
570
+
dependencies:
571
+
cross-spawn: 7.0.3
572
+
signal-exit: 4.1.0
573
+
574
+
fsevents@2.3.3:
575
+
optional: true
576
+
577
+
function-bind@1.1.2: {}
578
+
579
+
glob-parent@5.1.2:
580
+
dependencies:
581
+
is-glob: 4.0.3
582
+
583
+
glob-parent@6.0.2:
584
+
dependencies:
585
+
is-glob: 4.0.3
586
+
587
+
glob@10.4.5:
588
+
dependencies:
589
+
foreground-child: 3.2.1
590
+
jackspeak: 3.4.3
591
+
minimatch: 9.0.5
592
+
minipass: 7.1.2
593
+
package-json-from-dist: 1.0.0
594
+
path-scurry: 1.11.1
595
+
596
+
hasown@2.0.2:
597
+
dependencies:
598
+
function-bind: 1.1.2
599
+
600
+
is-binary-path@2.1.0:
601
+
dependencies:
602
+
binary-extensions: 2.3.0
603
+
604
+
is-core-module@2.15.0:
605
+
dependencies:
606
+
hasown: 2.0.2
607
+
608
+
is-extglob@2.1.1: {}
609
+
610
+
is-fullwidth-code-point@3.0.0: {}
611
+
612
+
is-glob@4.0.3:
613
+
dependencies:
614
+
is-extglob: 2.1.1
615
+
616
+
is-number@7.0.0: {}
617
+
618
+
isexe@2.0.0: {}
619
+
620
+
jackspeak@3.4.3:
621
+
dependencies:
622
+
'@isaacs/cliui': 8.0.2
623
+
optionalDependencies:
624
+
'@pkgjs/parseargs': 0.11.0
625
+
626
+
jiti@1.21.6: {}
627
+
628
+
lilconfig@2.1.0: {}
629
+
630
+
lilconfig@3.1.2: {}
631
+
632
+
lines-and-columns@1.2.4: {}
633
+
634
+
lru-cache@10.4.3: {}
635
+
636
+
merge2@1.4.1: {}
637
+
638
+
micromatch@4.0.7:
639
+
dependencies:
640
+
braces: 3.0.3
641
+
picomatch: 2.3.1
642
+
643
+
minimatch@9.0.5:
644
+
dependencies:
645
+
brace-expansion: 2.0.1
646
+
647
+
minipass@7.1.2: {}
648
+
649
+
mz@2.7.0:
650
+
dependencies:
651
+
any-promise: 1.3.0
652
+
object-assign: 4.1.1
653
+
thenify-all: 1.6.0
654
+
655
+
nanoid@3.3.7: {}
656
+
657
+
normalize-path@3.0.0: {}
658
+
659
+
object-assign@4.1.1: {}
660
+
661
+
object-hash@3.0.0: {}
662
+
663
+
package-json-from-dist@1.0.0: {}
664
+
665
+
path-key@3.1.1: {}
666
+
667
+
path-parse@1.0.7: {}
668
+
669
+
path-scurry@1.11.1:
670
+
dependencies:
671
+
lru-cache: 10.4.3
672
+
minipass: 7.1.2
673
+
674
+
picocolors@1.0.1: {}
675
+
676
+
picomatch@2.3.1: {}
677
+
678
+
pify@2.3.0: {}
679
+
680
+
pirates@4.0.6: {}
681
+
682
+
postcss-import@15.1.0(postcss@8.4.40):
683
+
dependencies:
684
+
postcss: 8.4.40
685
+
postcss-value-parser: 4.2.0
686
+
read-cache: 1.0.0
687
+
resolve: 1.22.8
688
+
689
+
postcss-js@4.0.1(postcss@8.4.40):
690
+
dependencies:
691
+
camelcase-css: 2.0.1
692
+
postcss: 8.4.40
693
+
694
+
postcss-load-config@4.0.2(postcss@8.4.40):
695
+
dependencies:
696
+
lilconfig: 3.1.2
697
+
yaml: 2.5.0
698
+
optionalDependencies:
699
+
postcss: 8.4.40
700
+
701
+
postcss-nested@6.2.0(postcss@8.4.40):
702
+
dependencies:
703
+
postcss: 8.4.40
704
+
postcss-selector-parser: 6.1.1
705
+
706
+
postcss-selector-parser@6.1.1:
707
+
dependencies:
708
+
cssesc: 3.0.0
709
+
util-deprecate: 1.0.2
710
+
711
+
postcss-value-parser@4.2.0: {}
712
+
713
+
postcss@8.4.40:
714
+
dependencies:
715
+
nanoid: 3.3.7
716
+
picocolors: 1.0.1
717
+
source-map-js: 1.2.0
718
+
719
+
queue-microtask@1.2.3: {}
720
+
721
+
read-cache@1.0.0:
722
+
dependencies:
723
+
pify: 2.3.0
724
+
725
+
readdirp@3.6.0:
726
+
dependencies:
727
+
picomatch: 2.3.1
728
+
729
+
resolve@1.22.8:
730
+
dependencies:
731
+
is-core-module: 2.15.0
732
+
path-parse: 1.0.7
733
+
supports-preserve-symlinks-flag: 1.0.0
734
+
735
+
reusify@1.0.4: {}
736
+
737
+
run-parallel@1.2.0:
738
+
dependencies:
739
+
queue-microtask: 1.2.3
740
+
741
+
shebang-command@2.0.0:
742
+
dependencies:
743
+
shebang-regex: 3.0.0
744
+
745
+
shebang-regex@3.0.0: {}
746
+
747
+
signal-exit@4.1.0: {}
748
+
749
+
source-map-js@1.2.0: {}
750
+
751
+
string-width@4.2.3:
752
+
dependencies:
753
+
emoji-regex: 8.0.0
754
+
is-fullwidth-code-point: 3.0.0
755
+
strip-ansi: 6.0.1
756
+
757
+
string-width@5.1.2:
758
+
dependencies:
759
+
eastasianwidth: 0.2.0
760
+
emoji-regex: 9.2.2
761
+
strip-ansi: 7.1.0
762
+
763
+
strip-ansi@6.0.1:
764
+
dependencies:
765
+
ansi-regex: 5.0.1
766
+
767
+
strip-ansi@7.1.0:
768
+
dependencies:
769
+
ansi-regex: 6.0.1
770
+
771
+
sucrase@3.35.0:
772
+
dependencies:
773
+
'@jridgewell/gen-mapping': 0.3.5
774
+
commander: 4.1.1
775
+
glob: 10.4.5
776
+
lines-and-columns: 1.2.4
777
+
mz: 2.7.0
778
+
pirates: 4.0.6
779
+
ts-interface-checker: 0.1.13
780
+
781
+
supports-preserve-symlinks-flag@1.0.0: {}
782
+
783
+
tailwindcss@3.4.7:
784
+
dependencies:
785
+
'@alloc/quick-lru': 5.2.0
786
+
arg: 5.0.2
787
+
chokidar: 3.6.0
788
+
didyoumean: 1.2.2
789
+
dlv: 1.1.3
790
+
fast-glob: 3.3.2
791
+
glob-parent: 6.0.2
792
+
is-glob: 4.0.3
793
+
jiti: 1.21.6
794
+
lilconfig: 2.1.0
795
+
micromatch: 4.0.7
796
+
normalize-path: 3.0.0
797
+
object-hash: 3.0.0
798
+
picocolors: 1.0.1
799
+
postcss: 8.4.40
800
+
postcss-import: 15.1.0(postcss@8.4.40)
801
+
postcss-js: 4.0.1(postcss@8.4.40)
802
+
postcss-load-config: 4.0.2(postcss@8.4.40)
803
+
postcss-nested: 6.2.0(postcss@8.4.40)
804
+
postcss-selector-parser: 6.1.1
805
+
resolve: 1.22.8
806
+
sucrase: 3.35.0
807
+
transitivePeerDependencies:
808
+
- ts-node
809
+
810
+
thenify-all@1.6.0:
811
+
dependencies:
812
+
thenify: 3.3.1
813
+
814
+
thenify@3.3.1:
815
+
dependencies:
816
+
any-promise: 1.3.0
817
+
818
+
to-regex-range@5.0.1:
819
+
dependencies:
820
+
is-number: 7.0.0
821
+
822
+
ts-interface-checker@0.1.13: {}
823
+
824
+
util-deprecate@1.0.2: {}
825
+
826
+
which@2.0.2:
827
+
dependencies:
828
+
isexe: 2.0.0
829
+
830
+
wrap-ansi@7.0.0:
831
+
dependencies:
832
+
ansi-styles: 4.3.0
833
+
string-width: 4.2.3
834
+
strip-ansi: 6.0.1
835
+
836
+
wrap-ansi@8.1.0:
837
+
dependencies:
838
+
ansi-styles: 6.2.1
839
+
string-width: 5.1.2
840
+
strip-ansi: 7.1.0
841
+
842
+
yaml@2.5.0: {}
+11
tailwind.config.js
+11
tailwind.config.js