summary refs log tree commit diff
path: root/vendor/go.mau.fi
diff options
context:
space:
mode:
authorEmile <git@emile.space>2024-10-25 15:55:50 +0200
committerEmile <git@emile.space>2024-10-25 15:55:50 +0200
commitc90f36e3dd179d2de96f4f5fe38d8dc9a9de6dfe (patch)
tree89e9afb41c5bf76f48cfb09305a2d3db8d302b06 /vendor/go.mau.fi
parent98bbb0f559a8883bc47bae80607dbe326a448e61 (diff)
vendor HEAD main
Diffstat (limited to 'vendor/go.mau.fi')
-rw-r--r--vendor/go.mau.fi/util/LICENSE373
-rw-r--r--vendor/go.mau.fi/util/base58/README.md9
-rw-r--r--vendor/go.mau.fi/util/base58/alphabet.go49
-rw-r--r--vendor/go.mau.fi/util/base58/base58.go138
-rw-r--r--vendor/go.mau.fi/util/base58/base58check.go52
-rw-r--r--vendor/go.mau.fi/util/base58/doc.go29
-rw-r--r--vendor/go.mau.fi/util/exerrors/dualerror.go33
-rw-r--r--vendor/go.mau.fi/util/exerrors/must.go23
-rw-r--r--vendor/go.mau.fi/util/exgjson/gjson.go33
-rw-r--r--vendor/go.mau.fi/util/exhttp/cors.go26
-rw-r--r--vendor/go.mau.fi/util/exhttp/handleerrors.go58
-rw-r--r--vendor/go.mau.fi/util/exhttp/json.go27
-rw-r--r--vendor/go.mau.fi/util/exhttp/middleware.go24
-rw-r--r--vendor/go.mau.fi/util/exzerolog/callermarshal.go28
-rw-r--r--vendor/go.mau.fi/util/exzerolog/defaults.go32
-rw-r--r--vendor/go.mau.fi/util/exzerolog/generics.go45
-rw-r--r--vendor/go.mau.fi/util/exzerolog/writer.go81
-rw-r--r--vendor/go.mau.fi/util/glob/glob.go100
-rw-r--r--vendor/go.mau.fi/util/glob/regex.go43
-rw-r--r--vendor/go.mau.fi/util/glob/simple.go62
-rw-r--r--vendor/go.mau.fi/util/glob/util.go42
-rw-r--r--vendor/go.mau.fi/util/jsonbytes/unpadded.go30
-rw-r--r--vendor/go.mau.fi/util/jsontime/helpers.go59
-rw-r--r--vendor/go.mau.fi/util/jsontime/integer.go163
-rw-r--r--vendor/go.mau.fi/util/jsontime/string.go95
-rw-r--r--vendor/go.mau.fi/util/ptr/ptr.go43
-rw-r--r--vendor/go.mau.fi/util/random/bytes.go21
-rw-r--r--vendor/go.mau.fi/util/random/string.go87
-rw-r--r--vendor/go.mau.fi/util/retryafter/retryafter.go53
29 files changed, 1858 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/util/LICENSE b/vendor/go.mau.fi/util/LICENSE
new file mode 100644
index 0000000..a612ad9
--- /dev/null
+++ b/vendor/go.mau.fi/util/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
diff --git a/vendor/go.mau.fi/util/base58/README.md b/vendor/go.mau.fi/util/base58/README.md
new file mode 100644
index 0000000..5095450
--- /dev/null
+++ b/vendor/go.mau.fi/util/base58/README.md
@@ -0,0 +1,9 @@
+base58
+==========
+
+This is a copy of <https://github.com/btcsuite/btcd/tree/master/btcutil/base58>.
+
+## License
+
+Package base58 is licensed under the [copyfree](http://copyfree.org) ISC
+License.
diff --git a/vendor/go.mau.fi/util/base58/alphabet.go b/vendor/go.mau.fi/util/base58/alphabet.go
new file mode 100644
index 0000000..6bb39fe
--- /dev/null
+++ b/vendor/go.mau.fi/util/base58/alphabet.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2015 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+// AUTOGENERATED by genalphabet.go; do not edit.
+
+package base58
+
+const (
+	// alphabet is the modified base58 alphabet used by Bitcoin.
+	alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+
+	alphabetIdx0 = '1'
+)
+
+var b58 = [256]byte{
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 0, 1, 2, 3, 4, 5, 6,
+	7, 8, 255, 255, 255, 255, 255, 255,
+	255, 9, 10, 11, 12, 13, 14, 15,
+	16, 255, 17, 18, 19, 20, 21, 255,
+	22, 23, 24, 25, 26, 27, 28, 29,
+	30, 31, 32, 255, 255, 255, 255, 255,
+	255, 33, 34, 35, 36, 37, 38, 39,
+	40, 41, 42, 43, 255, 44, 45, 46,
+	47, 48, 49, 50, 51, 52, 53, 54,
+	55, 56, 57, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255,
+}
diff --git a/vendor/go.mau.fi/util/base58/base58.go b/vendor/go.mau.fi/util/base58/base58.go
new file mode 100644
index 0000000..8ee5956
--- /dev/null
+++ b/vendor/go.mau.fi/util/base58/base58.go
@@ -0,0 +1,138 @@
+// Copyright (c) 2013-2015 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package base58
+
+import (
+	"math/big"
+)
+
+//go:generate go run genalphabet.go
+
+var bigRadix = [...]*big.Int{
+	big.NewInt(0),
+	big.NewInt(58),
+	big.NewInt(58 * 58),
+	big.NewInt(58 * 58 * 58),
+	big.NewInt(58 * 58 * 58 * 58),
+	big.NewInt(58 * 58 * 58 * 58 * 58),
+	big.NewInt(58 * 58 * 58 * 58 * 58 * 58),
+	big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58),
+	big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
+	big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
+	bigRadix10,
+}
+
+var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10
+
+// Decode decodes a modified base58 string to a byte slice.
+func Decode(b string) []byte {
+	answer := big.NewInt(0)
+	scratch := new(big.Int)
+
+	// Calculating with big.Int is slow for each iteration.
+	//    x += b58[b[i]] * j
+	//    j *= 58
+	//
+	// Instead we can try to do as much calculations on int64.
+	// We can represent a 10 digit base58 number using an int64.
+	//
+	// Hence we'll try to convert 10, base58 digits at a time.
+	// The rough idea is to calculate `t`, such that:
+	//
+	//   t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0
+	//   x *= 58^10
+	//   x += t
+	//
+	// Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10.
+	// In that case we'll use the bigRadix[n] lookup for the appropriate power.
+	for t := b; len(t) > 0; {
+		n := len(t)
+		if n > 10 {
+			n = 10
+		}
+
+		total := uint64(0)
+		for _, v := range t[:n] {
+			tmp := b58[v]
+			if tmp == 255 {
+				return []byte("")
+			}
+			total = total*58 + uint64(tmp)
+		}
+
+		answer.Mul(answer, bigRadix[n])
+		scratch.SetUint64(total)
+		answer.Add(answer, scratch)
+
+		t = t[n:]
+	}
+
+	tmpval := answer.Bytes()
+
+	var numZeros int
+	for numZeros = 0; numZeros < len(b); numZeros++ {
+		if b[numZeros] != alphabetIdx0 {
+			break
+		}
+	}
+	flen := numZeros + len(tmpval)
+	val := make([]byte, flen)
+	copy(val[numZeros:], tmpval)
+
+	return val
+}
+
+// Encode encodes a byte slice to a modified base58 string.
+func Encode(b []byte) string {
+	x := new(big.Int)
+	x.SetBytes(b)
+
+	// maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
+	maxlen := int(float64(len(b))*1.365658237309761) + 1
+	answer := make([]byte, 0, maxlen)
+	mod := new(big.Int)
+	for x.Sign() > 0 {
+		// Calculating with big.Int is slow for each iteration.
+		//    x, mod = x / 58, x % 58
+		//
+		// Instead we can try to do as much calculations on int64.
+		//    x, mod = x / 58^10, x % 58^10
+		//
+		// Which will give us mod, which is 10 digit base58 number.
+		// We'll loop that 10 times to convert to the answer.
+
+		x.DivMod(x, bigRadix10, mod)
+		if x.Sign() == 0 {
+			// When x = 0, we need to ensure we don't add any extra zeros.
+			m := mod.Int64()
+			for m > 0 {
+				answer = append(answer, alphabet[m%58])
+				m /= 58
+			}
+		} else {
+			m := mod.Int64()
+			for i := 0; i < 10; i++ {
+				answer = append(answer, alphabet[m%58])
+				m /= 58
+			}
+		}
+	}
+
+	// leading zero bytes
+	for _, i := range b {
+		if i != 0 {
+			break
+		}
+		answer = append(answer, alphabetIdx0)
+	}
+
+	// reverse
+	alen := len(answer)
+	for i := 0; i < alen/2; i++ {
+		answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
+	}
+
+	return string(answer)
+}
diff --git a/vendor/go.mau.fi/util/base58/base58check.go b/vendor/go.mau.fi/util/base58/base58check.go
new file mode 100644
index 0000000..402c323
--- /dev/null
+++ b/vendor/go.mau.fi/util/base58/base58check.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2013-2014 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package base58
+
+import (
+	"crypto/sha256"
+	"errors"
+)
+
+// ErrChecksum indicates that the checksum of a check-encoded string does not verify against
+// the checksum.
+var ErrChecksum = errors.New("checksum error")
+
+// ErrInvalidFormat indicates that the check-encoded string has an invalid format.
+var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
+
+// checksum: first four bytes of sha256^2
+func checksum(input []byte) (cksum [4]byte) {
+	h := sha256.Sum256(input)
+	h2 := sha256.Sum256(h[:])
+	copy(cksum[:], h2[:4])
+	return
+}
+
+// CheckEncode prepends a version byte and appends a four byte checksum.
+func CheckEncode(input []byte, version byte) string {
+	b := make([]byte, 0, 1+len(input)+4)
+	b = append(b, version)
+	b = append(b, input...)
+	cksum := checksum(b)
+	b = append(b, cksum[:]...)
+	return Encode(b)
+}
+
+// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
+func CheckDecode(input string) (result []byte, version byte, err error) {
+	decoded := Decode(input)
+	if len(decoded) < 5 {
+		return nil, 0, ErrInvalidFormat
+	}
+	version = decoded[0]
+	var cksum [4]byte
+	copy(cksum[:], decoded[len(decoded)-4:])
+	if checksum(decoded[:len(decoded)-4]) != cksum {
+		return nil, 0, ErrChecksum
+	}
+	payload := decoded[1 : len(decoded)-4]
+	result = append(result, payload...)
+	return
+}
diff --git a/vendor/go.mau.fi/util/base58/doc.go b/vendor/go.mau.fi/util/base58/doc.go
new file mode 100644
index 0000000..d657f05
--- /dev/null
+++ b/vendor/go.mau.fi/util/base58/doc.go
@@ -0,0 +1,29 @@
+// Copyright (c) 2014 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+/*
+Package base58 provides an API for working with modified base58 and Base58Check
+encodings.
+
+# Modified Base58 Encoding
+
+Standard base58 encoding is similar to standard base64 encoding except, as the
+name implies, it uses a 58 character alphabet which results in an alphanumeric
+string and allows some characters which are problematic for humans to be
+excluded.  Due to this, there can be various base58 alphabets.
+
+The modified base58 alphabet used by Bitcoin, and hence this package, omits the
+0, O, I, and l characters that look the same in many fonts and are therefore
+hard to humans to distinguish.
+
+# Base58Check Encoding Scheme
+
+The Base58Check encoding scheme is primarily used for Bitcoin addresses at the
+time of this writing, however it can be used to generically encode arbitrary
+byte arrays into human-readable strings along with a version byte that can be
+used to differentiate the same payload.  For Bitcoin addresses, the extra
+version is used to differentiate the network of otherwise identical public keys
+which helps prevent using an address intended for one network on another.
+*/
+package base58
diff --git a/vendor/go.mau.fi/util/exerrors/dualerror.go b/vendor/go.mau.fi/util/exerrors/dualerror.go
new file mode 100644
index 0000000..79acd06
--- /dev/null
+++ b/vendor/go.mau.fi/util/exerrors/dualerror.go
@@ -0,0 +1,33 @@
+// Copyright (c) 2022 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package exerrors
+
+import (
+	"errors"
+	"fmt"
+)
+
+type DualError struct {
+	High error
+	Low  error
+}
+
+func NewDualError(high, low error) DualError {
+	return DualError{high, low}
+}
+
+func (err DualError) Is(other error) bool {
+	return errors.Is(other, err.High) || errors.Is(other, err.Low)
+}
+
+func (err DualError) Unwrap() error {
+	return err.Low
+}
+
+func (err DualError) Error() string {
+	return fmt.Sprintf("%v: %v", err.High, err.Low)
+}
diff --git a/vendor/go.mau.fi/util/exerrors/must.go b/vendor/go.mau.fi/util/exerrors/must.go
new file mode 100644
index 0000000..2ffda30
--- /dev/null
+++ b/vendor/go.mau.fi/util/exerrors/must.go
@@ -0,0 +1,23 @@
+// Copyright (c) 2024 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package exerrors
+
+func Must[T any](val T, err error) T {
+	PanicIfNotNil(err)
+	return val
+}
+
+func Must2[T any, T2 any](val T, val2 T2, err error) (T, T2) {
+	PanicIfNotNil(err)
+	return val, val2
+}
+
+func PanicIfNotNil(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/vendor/go.mau.fi/util/exgjson/gjson.go b/vendor/go.mau.fi/util/exgjson/gjson.go
new file mode 100644
index 0000000..ee8fcdd
--- /dev/null
+++ b/vendor/go.mau.fi/util/exgjson/gjson.go
@@ -0,0 +1,33 @@
+// Copyright (c) 2022 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package exgjson
+
+import (
+	"strings"
+)
+
+// Escaper escapes a string for use in a GJSON path.
+var Escaper = strings.NewReplacer(
+	`\`, `\\`,
+	".", `\.`,
+	"|", `\|`,
+	"#", `\#`,
+	"@", `\@`,
+	"*", `\*`,
+	"?", `\?`)
+
+// Path returns a GJSON path pointing at a nested object, with each provided string being a key.
+func Path(path ...string) string {
+	var result strings.Builder
+	for i, part := range path {
+		_, _ = Escaper.WriteString(&result, part)
+		if i < len(path)-1 {
+			result.WriteRune('.')
+		}
+	}
+	return result.String()
+}
diff --git a/vendor/go.mau.fi/util/exhttp/cors.go b/vendor/go.mau.fi/util/exhttp/cors.go
new file mode 100644
index 0000000..037be8d
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/cors.go
@@ -0,0 +1,26 @@
+package exhttp
+
+import "net/http"
+
+func AddCORSHeaders(w http.ResponseWriter) {
+	// Recommended CORS headers can be found in https://spec.matrix.org/v1.3/client-server-api/#web-browser-clients
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
+	w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization")
+	w.Header().Set("Content-Security-Policy", "sandbox; default-src 'none'; script-src 'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src 'self';")
+	// Allow browsers to cache above for 1 day
+	w.Header().Set("Access-Control-Max-Age", "86400")
+}
+
+// CORSMiddleware adds CORS headers to the response and handles OPTIONS
+// requests by returning 200 OK immediately.
+func CORSMiddleware(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		AddCORSHeaders(w)
+		if r.Method == http.MethodOptions {
+			w.WriteHeader(http.StatusOK)
+			return
+		}
+		next.ServeHTTP(w, r)
+	})
+}
diff --git a/vendor/go.mau.fi/util/exhttp/handleerrors.go b/vendor/go.mau.fi/util/exhttp/handleerrors.go
new file mode 100644
index 0000000..d2d37b1
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/handleerrors.go
@@ -0,0 +1,58 @@
+package exhttp
+
+import "net/http"
+
+type ErrorBodyGenerators struct {
+	NotFound         func() []byte
+	MethodNotAllowed func() []byte
+}
+
+func HandleErrors(next http.Handler, gen ErrorBodyGenerators) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		next.ServeHTTP(&bodyOverrider{
+			ResponseWriter:                      w,
+			statusNotFoundBodyGenerator:         gen.NotFound,
+			statusMethodNotAllowedBodyGenerator: gen.MethodNotAllowed,
+		}, r)
+	})
+}
+
+type bodyOverrider struct {
+	http.ResponseWriter
+
+	code     int
+	override bool
+
+	statusNotFoundBodyGenerator         func() []byte
+	statusMethodNotAllowedBodyGenerator func() []byte
+}
+
+var _ http.ResponseWriter = (*bodyOverrider)(nil)
+
+func (b *bodyOverrider) WriteHeader(code int) {
+	if b.Header().Get("Content-Type") == "text/plain; charset=utf-8" {
+		b.Header().Set("Content-Type", "application/json")
+
+		b.override = true
+	}
+
+	b.code = code
+	b.ResponseWriter.WriteHeader(code)
+}
+
+func (b *bodyOverrider) Write(body []byte) (int, error) {
+	if b.override {
+		switch b.code {
+		case http.StatusNotFound:
+			if b.statusNotFoundBodyGenerator != nil {
+				body = b.statusNotFoundBodyGenerator()
+			}
+		case http.StatusMethodNotAllowed:
+			if b.statusMethodNotAllowedBodyGenerator != nil {
+				body = b.statusMethodNotAllowedBodyGenerator()
+			}
+		}
+	}
+
+	return b.ResponseWriter.Write(body)
+}
diff --git a/vendor/go.mau.fi/util/exhttp/json.go b/vendor/go.mau.fi/util/exhttp/json.go
new file mode 100644
index 0000000..48c8349
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/json.go
@@ -0,0 +1,27 @@
+package exhttp
+
+import (
+	"encoding/json"
+	"net/http"
+)
+
+func WriteJSONResponse(w http.ResponseWriter, httpStatusCode int, jsonData any) {
+	AddCORSHeaders(w)
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(httpStatusCode)
+	_ = json.NewEncoder(w).Encode(jsonData)
+}
+
+func WriteJSONData(w http.ResponseWriter, httpStatusCode int, data []byte) {
+	AddCORSHeaders(w)
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(httpStatusCode)
+	_, _ = w.Write(data)
+}
+
+func WriteEmptyJSONResponse(w http.ResponseWriter, httpStatusCode int) {
+	AddCORSHeaders(w)
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(httpStatusCode)
+	_, _ = w.Write([]byte("{}"))
+}
diff --git a/vendor/go.mau.fi/util/exhttp/middleware.go b/vendor/go.mau.fi/util/exhttp/middleware.go
new file mode 100644
index 0000000..733d348
--- /dev/null
+++ b/vendor/go.mau.fi/util/exhttp/middleware.go
@@ -0,0 +1,24 @@
+package exhttp
+
+import "net/http"
+
+// Middleware represents a middleware that can be applied to an [http.Handler].
+type Middleware func(http.Handler) http.Handler
+
+// ApplyMiddleware applies the provided [Middleware] functions to the given
+// router. The middlewares will be applied in the order they are provided.
+func ApplyMiddleware(router http.Handler, middlewares ...Middleware) http.Handler {
+	// Apply middlewares in reverse order because the first middleware provided
+	// needs to be the outermost one.
+	for i := len(middlewares) - 1; i >= 0; i-- {
+		router = middlewares[i](router)
+	}
+	return router
+}
+
+// StripPrefix is a wrapper for [http.StripPrefix] is compatible with the middleware pattern.
+func StripPrefix(prefix string) Middleware {
+	return func(next http.Handler) http.Handler {
+		return http.StripPrefix(prefix, next)
+	}
+}
diff --git a/vendor/go.mau.fi/util/exzerolog/callermarshal.go b/vendor/go.mau.fi/util/exzerolog/callermarshal.go
new file mode 100644
index 0000000..938a5e0
--- /dev/null
+++ b/vendor/go.mau.fi/util/exzerolog/callermarshal.go
@@ -0,0 +1,28 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package exzerolog
+
+import (
+	"fmt"
+	"runtime"
+	"strings"
+)
+
+// CallerWithFunctionName is an implementation for zerolog.CallerMarshalFunc that includes the caller function name
+// in addition to the file and line number.
+//
+// Use as
+//
+//	zerolog.CallerMarshalFunc = exzerolog.CallerWithFunctionName
+func CallerWithFunctionName(pc uintptr, file string, line int) string {
+	files := strings.Split(file, "/")
+	file = files[len(files)-1]
+	name := runtime.FuncForPC(pc).Name()
+	fns := strings.Split(name, ".")
+	name = fns[len(fns)-1]
+	return fmt.Sprintf("%s:%d:%s()", file, line, name)
+}
diff --git a/vendor/go.mau.fi/util/exzerolog/defaults.go b/vendor/go.mau.fi/util/exzerolog/defaults.go
new file mode 100644
index 0000000..c8c3c81
--- /dev/null
+++ b/vendor/go.mau.fi/util/exzerolog/defaults.go
@@ -0,0 +1,32 @@
+// Copyright (c) 2024 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package exzerolog
+
+import (
+	"time"
+
+	"github.com/rs/zerolog"
+	deflog "github.com/rs/zerolog/log"
+)
+
+// SetupDefaults updates zerolog globals with sensible defaults.
+//
+// * [zerolog.TimeFieldFormat] is set to time.RFC3339Nano instead of time.RFC3339
+// * [zerolog.CallerMarshalFunc] is set to [CallerWithFunctionName]
+// * [zerolog.DefaultContextLogger] is set to the given logger with default_context_log=true and caller info enabled
+// * The global default [log.Logger] is set to the given logger with global_log=true and caller info enabled
+// * [zerolog.LevelColors] are updated to swap trace and debug level colors
+func SetupDefaults(log *zerolog.Logger) {
+	zerolog.TimeFieldFormat = time.RFC3339Nano
+	zerolog.CallerMarshalFunc = CallerWithFunctionName
+	defaultCtxLog := log.With().Bool("default_context_log", true).Caller().Logger()
+	zerolog.DefaultContextLogger = &defaultCtxLog
+	deflog.Logger = log.With().Bool("global_log", true).Caller().Logger()
+	// Swap trace and debug level colors so trace pops out the least
+	zerolog.LevelColors[zerolog.TraceLevel] = 0
+	zerolog.LevelColors[zerolog.DebugLevel] = 34 // blue
+}
diff --git a/vendor/go.mau.fi/util/exzerolog/generics.go b/vendor/go.mau.fi/util/exzerolog/generics.go
new file mode 100644
index 0000000..ca1910b
--- /dev/null
+++ b/vendor/go.mau.fi/util/exzerolog/generics.go
@@ -0,0 +1,45 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package exzerolog
+
+import (
+	"fmt"
+
+	"github.com/rs/zerolog"
+)
+
+func ArrayOf[T any](slice []T, fn func(arr *zerolog.Array, item T)) *zerolog.Array {
+	arr := zerolog.Arr()
+	for _, item := range slice {
+		fn(arr, item)
+	}
+	return arr
+}
+
+func AddObject[T zerolog.LogObjectMarshaler](arr *zerolog.Array, obj T) {
+	arr.Object(obj)
+}
+
+func AddStringer[T fmt.Stringer](arr *zerolog.Array, str T) {
+	arr.Str(str.String())
+}
+
+func AddStr[T ~string](arr *zerolog.Array, str T) {
+	arr.Str(string(str))
+}
+
+func ArrayOfObjs[T zerolog.LogObjectMarshaler](slice []T) *zerolog.Array {
+	return ArrayOf(slice, AddObject[T])
+}
+
+func ArrayOfStringers[T fmt.Stringer](slice []T) *zerolog.Array {
+	return ArrayOf(slice, AddStringer[T])
+}
+
+func ArrayOfStrs[T ~string](slice []T) *zerolog.Array {
+	return ArrayOf(slice, AddStr[T])
+}
diff --git a/vendor/go.mau.fi/util/exzerolog/writer.go b/vendor/go.mau.fi/util/exzerolog/writer.go
new file mode 100644
index 0000000..c570985
--- /dev/null
+++ b/vendor/go.mau.fi/util/exzerolog/writer.go
@@ -0,0 +1,81 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package exzerolog
+
+import (
+	"bytes"
+	"sync"
+
+	"github.com/rs/zerolog"
+)
+
+// LogWriter wraps a zerolog.Logger and provides an io.Writer with buffering so each written line is logged separately.
+type LogWriter struct {
+	log   zerolog.Logger
+	level zerolog.Level
+	field string
+	mu    sync.Mutex
+	buf   bytes.Buffer
+}
+
+func NewLogWriter(log zerolog.Logger) *LogWriter {
+	zerolog.Nop()
+	return &LogWriter{
+		log:   log,
+		level: zerolog.DebugLevel,
+		field: zerolog.MessageFieldName,
+	}
+}
+
+func (lw *LogWriter) WithLevel(level zerolog.Level) *LogWriter {
+	return &LogWriter{
+		log:   lw.log,
+		level: level,
+		field: lw.field,
+	}
+}
+
+func (lw *LogWriter) WithField(field string) *LogWriter {
+	return &LogWriter{
+		log:   lw.log,
+		level: lw.level,
+		field: field,
+	}
+}
+
+func (lw *LogWriter) writeLine(data []byte) {
+	if len(data) == 0 {
+		return
+	}
+	if data[len(data)-1] == '\n' {
+		data = data[:len(data)-1]
+	}
+	if lw.buf.Len() > 0 {
+		lw.buf.Write(data)
+		data = lw.buf.Bytes()
+		lw.buf.Reset()
+	}
+	lw.log.WithLevel(lw.level).Bytes(lw.field, data).Send()
+}
+
+func (lw *LogWriter) Write(data []byte) (n int, err error) {
+	lw.mu.Lock()
+	defer lw.mu.Unlock()
+	newline := bytes.IndexByte(data, '\n')
+	if newline == len(data)-1 {
+		lw.writeLine(data)
+	} else if newline < 0 {
+		lw.buf.Write(data)
+	} else {
+		lines := bytes.Split(data, []byte{'\n'})
+		for _, line := range lines[:len(lines)-1] {
+			lw.writeLine(line)
+		}
+		lw.buf.Write(lines[len(lines)-1])
+	}
+	return len(data), nil
+}
diff --git a/vendor/go.mau.fi/util/glob/glob.go b/vendor/go.mau.fi/util/glob/glob.go
new file mode 100644
index 0000000..67fce3e
--- /dev/null
+++ b/vendor/go.mau.fi/util/glob/glob.go
@@ -0,0 +1,100 @@
+// Package glob implements very simple glob pattern matching used in various parts of the Matrix spec,
+// such as push rules and moderation policy lists.
+//
+// See https://spec.matrix.org/v1.11/appendices/#glob-style-matching for more info.
+package glob
+
+import (
+	"strings"
+)
+
+type Glob interface {
+	Match(string) bool
+}
+
+var (
+	_ Glob = ExactGlob("")
+	_ Glob = PrefixGlob("")
+	_ Glob = SuffixGlob("")
+	_ Glob = ContainsGlob("")
+	_ Glob = (*PrefixAndSuffixGlob)(nil)
+	_ Glob = (*PrefixSuffixAndContainsGlob)(nil)
+	_ Glob = (*RegexGlob)(nil)
+)
+
+// Compile compiles a glob pattern into an object that can be used to efficiently match strings against the pattern.
+//
+// Simple globs will be converted into prefix/suffix/contains checks, while complicated ones will be compiled as regex.
+func Compile(pattern string) Glob {
+	pattern = Simplify(pattern)
+	g := compileSimple(pattern)
+	if g != nil {
+		return g
+	}
+	g, _ = CompileRegex(pattern)
+	return g
+}
+
+// CompileWithImplicitContains is a wrapper for Compile which will replace exact matches with contains matches.
+// i.e. if the pattern has no wildcards, it will be treated as if it was surrounded in asterisks (`foo` -> `*foo*`).
+func CompileWithImplicitContains(pattern string) Glob {
+	g := Compile(pattern)
+	if _, isExact := g.(ExactGlob); isExact {
+		return ContainsGlob(pattern)
+	}
+	return g
+}
+
+// CompileSimple compiles a glob pattern into one of the non-regex forms.
+//
+// If the pattern can't be compiled into a simple form, it returns nil.
+func CompileSimple(pattern string) Glob {
+	return compileSimple(Simplify(pattern))
+}
+
+func compileSimple(pattern string) Glob {
+	if strings.ContainsRune(pattern, '?') {
+		return nil
+	}
+	switch strings.Count(pattern, "*") {
+	case 0:
+		return ExactGlob(pattern)
+	case 1:
+		if strings.HasPrefix(pattern, "*") {
+			return SuffixGlob(pattern[1:])
+		} else if strings.HasSuffix(pattern, "*") {
+			return PrefixGlob(pattern[:len(pattern)-1])
+		} else {
+			parts := strings.Split(pattern, "*")
+			return PrefixAndSuffixGlob{
+				Prefix: parts[0],
+				Suffix: parts[1],
+			}
+		}
+	case 2:
+		if strings.HasPrefix(pattern, "*") && strings.HasSuffix(pattern, "*") {
+			return ContainsGlob(pattern[1 : len(pattern)-1])
+		}
+		parts := strings.Split(pattern, "*")
+		return PrefixSuffixAndContainsGlob{
+			Prefix:   parts[0],
+			Contains: parts[1],
+			Suffix:   parts[2],
+		}
+	default:
+		return nil
+	}
+}
+
+var sqlCompiler = strings.NewReplacer(
+	`\`, `\\`,
+	`%`, `\%`,
+	`_`, `\_`,
+	`*`, `%`,
+	`?`, `_`,
+)
+
+// ToSQL converts a Matrix glob pattern to a SQL LIKE pattern.
+func ToSQL(pattern string) string {
+	return sqlCompiler.Replace(Simplify(pattern))
+}
diff --git a/vendor/go.mau.fi/util/glob/regex.go b/vendor/go.mau.fi/util/glob/regex.go
new file mode 100644
index 0000000..f224533
--- /dev/null
+++ b/vendor/go.mau.fi/util/glob/regex.go
@@ -0,0 +1,43 @@
+package glob
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+)
+
+type RegexGlob struct {
+	regex *regexp.Regexp
+}
+
+func (rg *RegexGlob) Match(s string) bool {
+	return rg.regex.MatchString(s)
+}
+
+func CompileRegex(pattern string) (*RegexGlob, error) {
+	var buf strings.Builder
+	buf.WriteRune('^')
+	for _, part := range SplitPattern(pattern) {
+		if strings.ContainsRune(part, '*') || strings.ContainsRune(part, '?') {
+			questions := strings.Count(part, "?")
+			star := strings.ContainsRune(part, '*')
+			if star {
+				if questions > 0 {
+					_, _ = fmt.Fprintf(&buf, ".{%d,}", questions)
+				} else {
+					buf.WriteString(".*")
+				}
+			} else if questions > 0 {
+				_, _ = fmt.Fprintf(&buf, ".{%d}", questions)
+			}
+		} else {
+			buf.WriteString(regexp.QuoteMeta(part))
+		}
+	}
+	buf.WriteRune('$')
+	regex, err := regexp.Compile(buf.String())
+	if err != nil {
+		return nil, err
+	}
+	return &RegexGlob{regex}, nil
+}
diff --git a/vendor/go.mau.fi/util/glob/simple.go b/vendor/go.mau.fi/util/glob/simple.go
new file mode 100644
index 0000000..bb9bf6d
--- /dev/null
+++ b/vendor/go.mau.fi/util/glob/simple.go
@@ -0,0 +1,62 @@
+package glob
+
+import (
+	"strings"
+)
+
+// ExactGlob is the result of [Compile] when the pattern contains no glob characters.
+// It uses a simple string comparison to match.
+type ExactGlob string
+
+func (eg ExactGlob) Match(s string) bool {
+	return string(eg) == s
+}
+
+// SuffixGlob is the result of [Compile] when the pattern only has one `*` at the beginning.
+// It uses [strings.HasSuffix] to match.
+type SuffixGlob string
+
+func (sg SuffixGlob) Match(s string) bool {
+	return strings.HasSuffix(s, string(sg))
+}
+
+// PrefixGlob is the result of [Compile] when the pattern only has one `*` at the end.
+// It uses [strings.HasPrefix] to match.
+type PrefixGlob string
+
+func (pg PrefixGlob) Match(s string) bool {
+	return strings.HasPrefix(s, string(pg))
+}
+
+// ContainsGlob is the result of [Compile] when the pattern has two `*`s, one at the beginning and one at the end.
+// It uses [strings.Contains] to match.
+//
+// When there are exactly two `*`s, but they're not surrounding the string, the pattern is compiled as a [PrefixSuffixAndContainsGlob] instead.
+type ContainsGlob string
+
+func (cg ContainsGlob) Match(s string) bool {
+	return strings.Contains(s, string(cg))
+}
+
+// PrefixAndSuffixGlob is the result of [Compile] when the pattern only has one `*` in the middle.
+type PrefixAndSuffixGlob struct {
+	Prefix string
+	Suffix string
+}
+
+func (psg PrefixAndSuffixGlob) Match(s string) bool {
+	return strings.HasPrefix(s, psg.Prefix) && strings.HasSuffix(s[len(psg.Prefix):], psg.Suffix)
+}
+
+// PrefixSuffixAndContainsGlob is the result of [Compile] when the pattern has two `*`s which are not surrounding the rest of the pattern.
+type PrefixSuffixAndContainsGlob struct {
+	Prefix   string
+	Suffix   string
+	Contains string
+}
+
+func (psacg PrefixSuffixAndContainsGlob) Match(s string) bool {
+	return strings.HasPrefix(s, psacg.Prefix) &&
+		strings.HasSuffix(s[len(psacg.Prefix):], psacg.Suffix) &&
+		strings.Contains(s[len(psacg.Prefix):len(s)-len(psacg.Suffix)], psacg.Contains)
+}
diff --git a/vendor/go.mau.fi/util/glob/util.go b/vendor/go.mau.fi/util/glob/util.go
new file mode 100644
index 0000000..37d9b2d
--- /dev/null
+++ b/vendor/go.mau.fi/util/glob/util.go
@@ -0,0 +1,42 @@
+package glob
+
+import (
+	"regexp"
+	"strings"
+)
+
+var redundantStarRegex = regexp.MustCompile(`\*{2,}`)
+var maybeRedundantQuestionRegex = regexp.MustCompile(`[*?]{2,}`)
+var wildcardRegex = regexp.MustCompile(`[*?]+`)
+
+func SplitPattern(pattern string) []string {
+	indexes := wildcardRegex.FindAllStringIndex(pattern, -1)
+	if len(indexes) == 0 {
+		return []string{pattern}
+	}
+	parts := make([]string, 0, len(indexes)+1)
+	start := 0
+	for _, part := range indexes {
+		end := part[0]
+		if end > start {
+			parts = append(parts, pattern[start:end])
+		}
+		parts = append(parts, pattern[part[0]:part[1]])
+		start = part[1]
+	}
+	if start < len(pattern) {
+		parts = append(parts, pattern[start:])
+	}
+	return parts
+}
+
+func Simplify(pattern string) string {
+	pattern = redundantStarRegex.ReplaceAllString(pattern, "*")
+	pattern = maybeRedundantQuestionRegex.ReplaceAllStringFunc(pattern, func(s string) string {
+		if !strings.ContainsRune(s, '*') {
+			return s
+		}
+		return strings.Repeat("?", strings.Count(s, "?")) + "*"
+	})
+	return pattern
+}
diff --git a/vendor/go.mau.fi/util/jsonbytes/unpadded.go b/vendor/go.mau.fi/util/jsonbytes/unpadded.go
new file mode 100644
index 0000000..37ffb64
--- /dev/null
+++ b/vendor/go.mau.fi/util/jsonbytes/unpadded.go
@@ -0,0 +1,30 @@
+// Copyright (c) 2024 Sumner Evans
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package jsonbytes
+
+import (
+	"encoding/base64"
+	"encoding/json"
+)
+
+// UnpaddedBytes is a byte slice that is encoded and decoded using
+// [base64.RawStdEncoding] instead of the default padded base64.
+type UnpaddedBytes []byte
+
+func (b UnpaddedBytes) MarshalJSON() ([]byte, error) {
+	return json.Marshal(base64.RawStdEncoding.EncodeToString(b))
+}
+
+func (b *UnpaddedBytes) UnmarshalJSON(data []byte) error {
+	var b64str string
+	err := json.Unmarshal(data, &b64str)
+	if err != nil {
+		return err
+	}
+	*b, err = base64.RawStdEncoding.DecodeString(b64str)
+	return err
+}
diff --git a/vendor/go.mau.fi/util/jsontime/helpers.go b/vendor/go.mau.fi/util/jsontime/helpers.go
new file mode 100644
index 0000000..35f4cc2
--- /dev/null
+++ b/vendor/go.mau.fi/util/jsontime/helpers.go
@@ -0,0 +1,59 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package jsontime
+
+import (
+	"time"
+)
+
+func UM(time time.Time) UnixMilli {
+	return UnixMilli{Time: time}
+}
+
+func UMInt(ts int64) UnixMilli {
+	return UM(time.UnixMilli(ts))
+}
+
+func UnixMilliNow() UnixMilli {
+	return UM(time.Now())
+}
+
+func UMicro(time time.Time) UnixMicro {
+	return UnixMicro{Time: time}
+}
+
+func UMicroInto(ts int64) UnixMicro {
+	return UMicro(time.UnixMicro(ts))
+}
+
+func UnixMicroNow() UnixMicro {
+	return UMicro(time.Now())
+}
+
+func UN(time time.Time) UnixNano {
+	return UnixNano{Time: time}
+}
+
+func UNInt(ts int64) UnixNano {
+	return UN(time.Unix(0, ts))
+}
+
+func UnixNanoNow() UnixNano {
+	return UN(time.Now())
+}
+
+func U(time time.Time) Unix {
+	return Unix{Time: time}
+}
+
+func UInt(ts int64) Unix {
+	return U(time.Unix(ts, 0))
+}
+
+func UnixNow() Unix {
+	return U(time.Now())
+}
diff --git a/vendor/go.mau.fi/util/jsontime/integer.go b/vendor/go.mau.fi/util/jsontime/integer.go
new file mode 100644
index 0000000..7d15d5d
--- /dev/null
+++ b/vendor/go.mau.fi/util/jsontime/integer.go
@@ -0,0 +1,163 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package jsontime
+
+import (
+	"database/sql"
+	"database/sql/driver"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"time"
+)
+
+var ErrNotInteger = errors.New("value is not an integer")
+
+func parseTime(data []byte, unixConv func(int64) time.Time, into *time.Time) error {
+	var val int64
+	err := json.Unmarshal(data, &val)
+	if err != nil {
+		return err
+	}
+	if val == 0 {
+		*into = time.Time{}
+	} else {
+		*into = unixConv(val)
+	}
+	return nil
+}
+
+func anyIntegerToTime(src any, unixConv func(int64) time.Time, into *time.Time) error {
+	switch v := src.(type) {
+	case int:
+		*into = unixConv(int64(v))
+	case int8:
+		*into = unixConv(int64(v))
+	case int16:
+		*into = unixConv(int64(v))
+	case int32:
+		*into = unixConv(int64(v))
+	case int64:
+		*into = unixConv(int64(v))
+	default:
+		return fmt.Errorf("%w: %T", ErrNotInteger, src)
+	}
+
+	return nil
+}
+
+var _ sql.Scanner = &UnixMilli{}
+var _ driver.Valuer = UnixMilli{}
+
+type UnixMilli struct {
+	time.Time
+}
+
+func (um UnixMilli) MarshalJSON() ([]byte, error) {
+	if um.IsZero() {
+		return []byte{'0'}, nil
+	}
+	return json.Marshal(um.UnixMilli())
+}
+
+func (um *UnixMilli) UnmarshalJSON(data []byte) error {
+	return parseTime(data, time.UnixMilli, &um.Time)
+}
+
+func (um UnixMilli) Value() (driver.Value, error) {
+	return um.UnixMilli(), nil
+}
+
+func (um *UnixMilli) Scan(src any) error {
+	return anyIntegerToTime(src, time.UnixMilli, &um.Time)
+}
+
+var _ sql.Scanner = &UnixMicro{}
+var _ driver.Valuer = UnixMicro{}
+
+type UnixMicro struct {
+	time.Time
+}
+
+func (um UnixMicro) MarshalJSON() ([]byte, error) {
+	if um.IsZero() {
+		return []byte{'0'}, nil
+	}
+	return json.Marshal(um.UnixMicro())
+}
+
+func (um *UnixMicro) UnmarshalJSON(data []byte) error {
+	return parseTime(data, time.UnixMicro, &um.Time)
+}
+
+func (um UnixMicro) Value() (driver.Value, error) {
+	return um.UnixMicro(), nil
+}
+
+func (um *UnixMicro) Scan(src any) error {
+	return anyIntegerToTime(src, time.UnixMicro, &um.Time)
+}
+
+var _ sql.Scanner = &UnixNano{}
+var _ driver.Valuer = UnixNano{}
+
+type UnixNano struct {
+	time.Time
+}
+
+func (un UnixNano) MarshalJSON() ([]byte, error) {
+	if un.IsZero() {
+		return []byte{'0'}, nil
+	}
+	return json.Marshal(un.UnixNano())
+}
+
+func (un *UnixNano) UnmarshalJSON(data []byte) error {
+	return parseTime(data, func(i int64) time.Time {
+		return time.Unix(0, i)
+	}, &un.Time)
+}
+
+func (un UnixNano) Value() (driver.Value, error) {
+	return un.UnixNano(), nil
+}
+
+func (un *UnixNano) Scan(src any) error {
+	return anyIntegerToTime(src, func(i int64) time.Time {
+		return time.Unix(0, i)
+	}, &un.Time)
+}
+
+type Unix struct {
+	time.Time
+}
+
+func (u Unix) MarshalJSON() ([]byte, error) {
+	if u.IsZero() {
+		return []byte{'0'}, nil
+	}
+	return json.Marshal(u.Unix())
+}
+
+var _ sql.Scanner = &Unix{}
+var _ driver.Valuer = Unix{}
+
+func (u *Unix) UnmarshalJSON(data []byte) error {
+	return parseTime(data, func(i int64) time.Time {
+		return time.Unix(i, 0)
+	}, &u.Time)
+}
+
+func (u Unix) Value() (driver.Value, error) {
+	return u.Unix(), nil
+}
+
+func (u *Unix) Scan(src any) error {
+	return anyIntegerToTime(src, func(i int64) time.Time {
+		return time.Unix(i, 0)
+	}, &u.Time)
+}
diff --git a/vendor/go.mau.fi/util/jsontime/string.go b/vendor/go.mau.fi/util/jsontime/string.go
new file mode 100644
index 0000000..c3729d5
--- /dev/null
+++ b/vendor/go.mau.fi/util/jsontime/string.go
@@ -0,0 +1,95 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package jsontime
+
+import (
+	"encoding/json"
+	"strconv"
+	"time"
+)
+
+func parseTimeString(data []byte, unixConv func(int64) time.Time, into *time.Time) error {
+	var strVal string
+	err := json.Unmarshal(data, &strVal)
+	if err != nil {
+		return err
+	}
+	val, err := strconv.ParseInt(strVal, 10, 64)
+	if err != nil {
+		return err
+	}
+	if val == 0 {
+		*into = time.Time{}
+	} else {
+		*into = unixConv(val)
+	}
+	return nil
+}
+
+type UnixMilliString struct {
+	time.Time
+}
+
+func (um UnixMilliString) MarshalJSON() ([]byte, error) {
+	if um.IsZero() {
+		return []byte{'"', '0', '"'}, nil
+	}
+	return json.Marshal(strconv.FormatInt(um.UnixMilli(), 10))
+}
+
+func (um *UnixMilliString) UnmarshalJSON(data []byte) error {
+	return parseTimeString(data, time.UnixMilli, &um.Time)
+}
+
+type UnixMicroString struct {
+	time.Time
+}
+
+func (um UnixMicroString) MarshalJSON() ([]byte, error) {
+	if um.IsZero() {
+		return []byte{'"', '0', '"'}, nil
+	}
+	return json.Marshal(strconv.FormatInt(um.UnixMicro(), 10))
+}
+
+func (um *UnixMicroString) UnmarshalJSON(data []byte) error {
+	return parseTimeString(data, time.UnixMicro, &um.Time)
+}
+
+type UnixNanoString struct {
+	time.Time
+}
+
+func (um UnixNanoString) MarshalJSON() ([]byte, error) {
+	if um.IsZero() {
+		return []byte{'"', '0', '"'}, nil
+	}
+	return json.Marshal(strconv.FormatInt(um.UnixNano(), 10))
+}
+
+func (um *UnixNanoString) UnmarshalJSON(data []byte) error {
+	return parseTimeString(data, func(i int64) time.Time {
+		return time.Unix(0, i)
+	}, &um.Time)
+}
+
+type UnixString struct {
+	time.Time
+}
+
+func (u UnixString) MarshalJSON() ([]byte, error) {
+	if u.IsZero() {
+		return []byte{'"', '0', '"'}, nil
+	}
+	return json.Marshal(strconv.FormatInt(u.Unix(), 10))
+}
+
+func (u *UnixString) UnmarshalJSON(data []byte) error {
+	return parseTimeString(data, func(i int64) time.Time {
+		return time.Unix(i, 0)
+	}, &u.Time)
+}
diff --git a/vendor/go.mau.fi/util/ptr/ptr.go b/vendor/go.mau.fi/util/ptr/ptr.go
new file mode 100644
index 0000000..b04fa19
--- /dev/null
+++ b/vendor/go.mau.fi/util/ptr/ptr.go
@@ -0,0 +1,43 @@
+// Copyright (c) 2024 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package ptr
+
+// Clone creates a shallow copy of the given pointer.
+func Clone[T any](val *T) *T {
+	if val == nil {
+		return nil
+	}
+	valCopy := *val
+	return &valCopy
+}
+
+// Ptr returns a pointer to the given value.
+func Ptr[T any](val T) *T {
+	return &val
+}
+
+// NonZero returns a pointer to the given comparable value, unless the value is the type's zero value.
+func NonZero[T comparable](val T) *T {
+	var zero T
+	return NonDefault(val, zero)
+}
+
+// NonDefault returns a pointer to the first parameter, unless it is equal to the second parameter.
+func NonDefault[T comparable](val, def T) *T {
+	if val == def {
+		return nil
+	}
+	return &val
+}
+
+// Val returns the value of the given pointer, or the zero value of the type if the pointer is nil.
+func Val[T any](ptr *T) (val T) {
+	if ptr != nil {
+		val = *ptr
+	}
+	return
+}
diff --git a/vendor/go.mau.fi/util/random/bytes.go b/vendor/go.mau.fi/util/random/bytes.go
new file mode 100644
index 0000000..c3a706b
--- /dev/null
+++ b/vendor/go.mau.fi/util/random/bytes.go
@@ -0,0 +1,21 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package random
+
+import (
+	"crypto/rand"
+)
+
+// Bytes generates the given amount of random bytes using crypto/rand, and panics if it fails.
+func Bytes(n int) []byte {
+	data := make([]byte, n)
+	_, err := rand.Read(data)
+	if err != nil {
+		panic(err)
+	}
+	return data
+}
diff --git a/vendor/go.mau.fi/util/random/string.go b/vendor/go.mau.fi/util/random/string.go
new file mode 100644
index 0000000..b9cb0ae
--- /dev/null
+++ b/vendor/go.mau.fi/util/random/string.go
@@ -0,0 +1,87 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package random
+
+import (
+	"encoding/binary"
+	"hash/crc32"
+	"strings"
+	"unsafe"
+)
+
+const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+// StringBytes generates a random string of the given length and returns it as a byte array.
+func StringBytes(n int) []byte {
+	if n <= 0 {
+		return []byte{}
+	}
+	input := Bytes(n * 2)
+	for i := 0; i < n; i++ {
+		// Risk of modulo bias is only 2 in 65535, values between 0 and 65533 are uniformly distributed
+		input[i] = letters[binary.BigEndian.Uint16(input[i*2:])%uint16(len(letters))]
+	}
+	input = input[:n]
+	return input
+}
+
+// String generates a random string of the given length.
+func String(n int) string {
+	if n <= 0 {
+		return ""
+	}
+	str := StringBytes(n)
+	return *(*string)(unsafe.Pointer(&str))
+}
+
+func base62Encode(val uint32, minWidth int) []byte {
+	out := make([]byte, 0, minWidth)
+	for val > 0 {
+		out = append(out, letters[val%uint32(len(letters))])
+		val /= 62
+	}
+	if len(out) < minWidth {
+		paddedOut := make([]byte, minWidth)
+		copy(paddedOut[minWidth-len(out):], out)
+		for i := 0; i < minWidth-len(out); i++ {
+			paddedOut[i] = '0'
+		}
+		out = paddedOut
+	}
+	return out
+}
+
+// Token generates a GitHub-style token with the given prefix, a random part, and a checksum at the end.
+// The format is `prefix_random_checksum`. The checksum is always 6 characters.
+func Token(namespace string, randomLength int) string {
+	token := make([]byte, len(namespace)+1+randomLength+1+6)
+	copy(token, namespace)
+	token[len(namespace)] = '_'
+	copy(token[len(namespace)+1:], StringBytes(randomLength))
+	token[len(namespace)+randomLength+1] = '_'
+	checksum := base62Encode(crc32.ChecksumIEEE(token[:len(token)-7]), 6)
+	copy(token[len(token)-6:], checksum)
+	return *(*string)(unsafe.Pointer(&token))
+}
+
+// GetTokenPrefix parses the given token generated with Token, validates the checksum and returns the prefix namespace.
+func GetTokenPrefix(token string) string {
+	parts := strings.Split(token, "_")
+	if len(parts) != 3 {
+		return ""
+	}
+	checksum := base62Encode(crc32.ChecksumIEEE([]byte(parts[0]+"_"+parts[1])), 6)
+	if string(checksum) != parts[2] {
+		return ""
+	}
+	return parts[0]
+}
+
+// IsToken checks if the given token is a valid token generated with Token with the given namespace..
+func IsToken(namespace, token string) bool {
+	return GetTokenPrefix(token) == namespace
+}
diff --git a/vendor/go.mau.fi/util/retryafter/retryafter.go b/vendor/go.mau.fi/util/retryafter/retryafter.go
new file mode 100644
index 0000000..57ec814
--- /dev/null
+++ b/vendor/go.mau.fi/util/retryafter/retryafter.go
@@ -0,0 +1,53 @@
+// Copyright (c) 2021 Dillon Dixon
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// Package retryafter contains a utility function for parsing the Retry-After HTTP header.
+package retryafter
+
+import (
+	"net/http"
+	"strconv"
+	"time"
+)
+
+var now = time.Now
+
+// Parse parses the backoff time specified in the Retry-After header if present.
+// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After.
+//
+// The second parameter is the fallback duration to use if the header is not present or invalid.
+//
+// Example:
+//
+//	time.Sleep(retryafter.Parse(resp.Header.Get("Retry-After"), 5*time.Second))
+func Parse(retryAfter string, fallback time.Duration) time.Duration {
+	if retryAfter == "" {
+		return fallback
+	} else if t, err := time.Parse(http.TimeFormat, retryAfter); err == nil {
+		return t.Sub(now())
+	} else if seconds, err := strconv.Atoi(retryAfter); err == nil {
+		return time.Duration(seconds) * time.Second
+	}
+
+	return fallback
+}
+
+// Should returns true if the given status code indicates that the request should be retried.
+//
+//	if retryafter.Should(resp.StatusCode, true) {
+//		time.Sleep(retryafter.Parse(resp.Header.Get("Retry-After"), 5*time.Second))
+//	}
+func Should(statusCode int, retryOnRateLimit bool) bool {
+	switch statusCode {
+	case http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout:
+		return true
+	case http.StatusTooManyRequests:
+		return retryOnRateLimit
+	default:
+		return false
+	}
+}