p1: /v2/scrape, /v1/fetch, auth, CLI, contract tests
Single Fetcher.Fetch on the Guard that holds DenyPrefixes. skipTlsVerification is ignored. No 502. keys add writes the secret only to --out. Release binary has no -test-allow-prefix.
This commit is contained in:
Generated
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
package textutils
|
||||
|
||||
import "strings"
|
||||
|
||||
func CalculateCodeFenceOccurrences(fenceChar rune, content string) int {
|
||||
var occurrences []int
|
||||
|
||||
var charsTogether int
|
||||
for _, char := range content {
|
||||
// We encountered a fence character, now count how many
|
||||
// are directly afterwards
|
||||
if char == fenceChar {
|
||||
charsTogether++
|
||||
} else if charsTogether != 0 {
|
||||
occurrences = append(occurrences, charsTogether)
|
||||
charsTogether = 0
|
||||
}
|
||||
}
|
||||
|
||||
// If the last element in the content was a fenceChar
|
||||
if charsTogether != 0 {
|
||||
occurrences = append(occurrences, charsTogether)
|
||||
}
|
||||
|
||||
return findMax(occurrences)
|
||||
}
|
||||
|
||||
// CalculateCodeFence can be passed the content of a code block and it returns
|
||||
// how many fence characters (` or ~) should be used.
|
||||
//
|
||||
// This is useful if the html content includes the same fence characters
|
||||
// for example ```
|
||||
// -> https://stackoverflow.com/a/49268657
|
||||
func CalculateCodeFence(fenceChar rune, content string) string {
|
||||
repeat := CalculateCodeFenceOccurrences(fenceChar, content)
|
||||
|
||||
// The outer fence block always has to have
|
||||
// at least one character more than any content inside
|
||||
repeat++
|
||||
|
||||
// You have to have at least three fence characters
|
||||
// to be recognized as a code block
|
||||
if repeat < 3 {
|
||||
repeat = 3
|
||||
}
|
||||
|
||||
return strings.Repeat(string(fenceChar), repeat)
|
||||
}
|
||||
|
||||
func findMax(a []int) (max int) {
|
||||
for i, value := range a {
|
||||
if i == 0 {
|
||||
max = a[i]
|
||||
}
|
||||
|
||||
if value > max {
|
||||
max = value
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
Generated
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
package textutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
func CollapseInlineCodeContent(content []byte) []byte {
|
||||
// TODO: what about other characters like the reset char? Maybe unicode.IsSpace?
|
||||
content = bytes.ReplaceAll(content, []byte{'\n'}, []byte{' '})
|
||||
content = bytes.ReplaceAll(content, []byte{'\t'}, []byte{' '})
|
||||
|
||||
content = bytes.TrimSpace(content)
|
||||
|
||||
newChars := make([]byte, 0, len(content))
|
||||
|
||||
var count int
|
||||
for _, char := range content {
|
||||
if char == ' ' {
|
||||
count++
|
||||
} else {
|
||||
count = 0
|
||||
}
|
||||
|
||||
if count > 1 {
|
||||
continue
|
||||
}
|
||||
newChars = append(newChars, char)
|
||||
}
|
||||
|
||||
return newChars
|
||||
}
|
||||
Generated
Vendored
+139
@@ -0,0 +1,139 @@
|
||||
package textutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TrimUnnecessaryHardLineBreaks(content []byte) []byte {
|
||||
content = bytes.ReplaceAll(content, []byte(" \n\n"), []byte("\n\n"))
|
||||
content = bytes.ReplaceAll(content, []byte(" \n \n"), []byte("\n\n"))
|
||||
content = bytes.ReplaceAll(content, []byte(" \n \n"), []byte("\n\n"))
|
||||
// out = bytes.ReplaceAll(out, []byte("\n \n"), []byte("\n\n"))
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
func TrimConsecutiveNewlines(input []byte) []byte {
|
||||
var result []byte
|
||||
newlineCount := 0
|
||||
spaceBuffer := []byte{}
|
||||
|
||||
for i := 0; i < len(input); {
|
||||
r, size := utf8.DecodeRune(input[i:])
|
||||
|
||||
if r == '\n' {
|
||||
newlineCount++
|
||||
if newlineCount <= 2 {
|
||||
// Preserve up to 2 newlines, including preceding spaces
|
||||
result = append(result, spaceBuffer...)
|
||||
result = append(result, '\n')
|
||||
spaceBuffer = spaceBuffer[:0] // Clear space buffer
|
||||
} else {
|
||||
// Skip additional newlines
|
||||
spaceBuffer = spaceBuffer[:0] // Clear space buffer
|
||||
}
|
||||
} else if r == ' ' {
|
||||
// Collect spaces into the space buffer
|
||||
spaceBuffer = append(spaceBuffer, input[i:i+size]...)
|
||||
} else {
|
||||
// Reset newline count and append non-newline characters
|
||||
newlineCount = 0
|
||||
result = append(result, spaceBuffer...)
|
||||
result = append(result, input[i:i+size]...)
|
||||
spaceBuffer = spaceBuffer[:0] // Clear space buffer
|
||||
}
|
||||
|
||||
i += size
|
||||
}
|
||||
|
||||
// Append any trailing spaces
|
||||
result = append(result, spaceBuffer...)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
func TrimConsecutiveNewlines(source []byte) []byte {
|
||||
// Some performance optimizations:
|
||||
// - If no replacement was done, we return the original slice and don't allocate.
|
||||
// - We batch appends
|
||||
|
||||
var ret []byte
|
||||
|
||||
startNormal := 0
|
||||
startMatch := -1
|
||||
|
||||
count := 0
|
||||
// for i, b := range source {
|
||||
for i := 0; i < len(source); i++ {
|
||||
r, size := utf8.DecodeRune(source[i:])
|
||||
_ = size
|
||||
|
||||
isNewline := r == '\n' // || r == marker.MarkerLineBreak
|
||||
if isNewline {
|
||||
count += 1
|
||||
}
|
||||
|
||||
if startMatch == -1 && isNewline {
|
||||
// Start of newlines
|
||||
startMatch = i
|
||||
i = i + size - 1
|
||||
continue
|
||||
} else if startMatch != -1 && isNewline {
|
||||
// Middle of newlines
|
||||
i = i + size - 1
|
||||
continue
|
||||
} else if startMatch != -1 {
|
||||
// Character after the last newline character
|
||||
|
||||
if count > 2 {
|
||||
if ret == nil {
|
||||
ret = make([]byte, 0, len(source))
|
||||
}
|
||||
|
||||
ret = append(ret, source[startNormal:startMatch]...)
|
||||
ret = append(ret, '\n', '\n')
|
||||
startNormal = i
|
||||
}
|
||||
|
||||
startMatch = -1
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
|
||||
getStartEnd := func() (int, int, bool, bool) {
|
||||
if startMatch == -1 && startNormal == 0 {
|
||||
// a) no changes need to be done
|
||||
return -1, -1, false, false
|
||||
}
|
||||
|
||||
if count <= 2 {
|
||||
// b) Only the normal characters still need to be added
|
||||
return startNormal, len(source), true, false
|
||||
}
|
||||
|
||||
// c) The match still needs to be replaced (and possible the previous normal characters be added)
|
||||
return startNormal, startMatch, true, true
|
||||
}
|
||||
|
||||
start, end, isKeepNeeded, isReplaceNeeded := getStartEnd()
|
||||
if isKeepNeeded {
|
||||
if ret == nil {
|
||||
ret = make([]byte, 0, len(source))
|
||||
}
|
||||
|
||||
ret = append(ret, source[start:end]...)
|
||||
if isReplaceNeeded {
|
||||
ret = append(ret, '\n', '\n')
|
||||
}
|
||||
}
|
||||
|
||||
if ret == nil {
|
||||
// Hurray, we did not do any allocations with make()
|
||||
// and instead just return the original slice.
|
||||
return source
|
||||
}
|
||||
return ret
|
||||
}
|
||||
*/
|
||||
Generated
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
package textutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// DelimiterForEveryLine puts the delimiter not just at the start and end of the string
|
||||
// but if the text is divided on multiple lines, puts the delimiters on every line with content.
|
||||
//
|
||||
// Otherwise the bold/italic delimiters won't be recognized if it contains new line characters.
|
||||
func DelimiterForEveryLine(text []byte, delimiter []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
|
||||
lines := bytes.Split(text, []byte("\n"))
|
||||
for i, line := range lines {
|
||||
leftExtra, trimmed, rightExtra := SurroundingSpaces(line)
|
||||
|
||||
if trimmed == nil {
|
||||
// For empty lines, we don't need a delimiter
|
||||
buf.Write(leftExtra)
|
||||
buf.Write(rightExtra)
|
||||
} else {
|
||||
buf.Write(leftExtra)
|
||||
buf.Write(delimiter)
|
||||
buf.Write(trimmed)
|
||||
buf.Write(delimiter)
|
||||
buf.Write(rightExtra)
|
||||
}
|
||||
|
||||
// To join the lines again, add a newlines character
|
||||
if i < len(lines)-1 {
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
Generated
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
package textutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
doubleSpace = []byte{' ', ' '}
|
||||
|
||||
newlineBreak = []byte{'\n'}
|
||||
hardLineBreak = []byte{' ', ' ', '\n'}
|
||||
escapedNoContentLineBreak = []byte{'\\', '\n'}
|
||||
)
|
||||
|
||||
// EscapeMultiLine deals with multiline content inside a link or a heading.
|
||||
func EscapeMultiLine(content []byte) []byte {
|
||||
parts := bytes.Split(content, newlineBreak)
|
||||
if len(parts) == 1 {
|
||||
return content
|
||||
}
|
||||
|
||||
output := make([]byte, 0, len(content))
|
||||
for i := range parts {
|
||||
trimmedLeft := bytes.TrimLeftFunc(parts[i], unicode.IsSpace)
|
||||
|
||||
if len(trimmedLeft) == 0 {
|
||||
// A blank line would interrupt the link.
|
||||
// So we need to escape the line
|
||||
output = append(output, escapedNoContentLineBreak...)
|
||||
continue
|
||||
}
|
||||
|
||||
isLast := i == len(parts)-1
|
||||
if isLast {
|
||||
// For the last line we don't need to add any "\n" anymore
|
||||
output = append(output, trimmedLeft...)
|
||||
continue
|
||||
}
|
||||
|
||||
// Now decide what ending we want:
|
||||
if bytes.HasSuffix(trimmedLeft, doubleSpace) {
|
||||
// We already have " " so adding a "\n" is enough
|
||||
output = append(output, trimmedLeft...)
|
||||
output = append(output, newlineBreak...)
|
||||
continue
|
||||
} else {
|
||||
// We *prefer* having a hard-line-break " \n"
|
||||
output = append(output, trimmedLeft...)
|
||||
output = append(output, hardLineBreak...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
package textutils
|
||||
|
||||
func PrefixLines(source []byte, repl []byte) []byte {
|
||||
newSlice := make([]byte, 0, len(source))
|
||||
|
||||
newSlice = append(newSlice, repl...)
|
||||
for _, b := range source {
|
||||
newSlice = append(newSlice, b)
|
||||
if b == '\n' {
|
||||
newSlice = append(newSlice, repl...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return newSlice
|
||||
}
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
package textutils
|
||||
|
||||
import "bytes"
|
||||
|
||||
const (
|
||||
DOUBLE_QUOTE = '"'
|
||||
SINGLE_QUOTE = '\''
|
||||
)
|
||||
|
||||
func SurroundBy(content []byte, chars []byte) []byte {
|
||||
content = append(chars, content...)
|
||||
content = append(content, chars...)
|
||||
return content
|
||||
}
|
||||
func SurroundByQuotes(content []byte) []byte {
|
||||
if len(content) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
containsDoubleQuote := bytes.ContainsRune(content, DOUBLE_QUOTE)
|
||||
containsSingleQuote := bytes.ContainsRune(content, SINGLE_QUOTE)
|
||||
|
||||
if containsDoubleQuote && containsSingleQuote {
|
||||
// Escape all quotes
|
||||
content = bytes.ReplaceAll(content, []byte(`"`), []byte(`\"`))
|
||||
|
||||
// Surround the content by double quotes
|
||||
return SurroundBy(content, []byte(`"`))
|
||||
}
|
||||
if containsDoubleQuote {
|
||||
// Since it contains double quotes (but no single quotes)
|
||||
// we can surround it by single quotes
|
||||
return SurroundBy(content, []byte(`'`))
|
||||
}
|
||||
|
||||
// It may contain single quotes, but definitely no double quotes,
|
||||
// so we can safely surround it by double quotes.
|
||||
return SurroundBy(content, []byte(`"`))
|
||||
}
|
||||
Generated
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
|
||||
The logic to handle whitespace around delimiters was initially developed
|
||||
in the fork from "anyproto" by Roman Khafizianov and Mikhail.
|
||||
|
||||
The changes were then merged upstream by Johannes Kaufmann.
|
||||
|
||||
https://github.com/anyproto/html-to-markdown
|
||||
https://github.com/JohannesKaufmann/html-to-markdown
|
||||
|
||||
-----------
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Johannes Kaufmann
|
||||
Copyright (c) 2020 Roman Khafizianov
|
||||
Copyright (c) 2023 Mikhail
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
package textutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func SurroundingSpaces(content []byte) ([]byte, []byte, []byte) {
|
||||
rightTrimmed := bytes.TrimRightFunc(content, func(r rune) bool {
|
||||
return unicode.IsSpace(r)
|
||||
})
|
||||
rightExtra := content[len(rightTrimmed):]
|
||||
|
||||
trimmed := bytes.TrimLeftFunc(rightTrimmed, func(r rune) bool {
|
||||
return unicode.IsSpace(r)
|
||||
})
|
||||
leftExtra := content[0 : len(rightTrimmed)-len(trimmed)]
|
||||
|
||||
return leftExtra, trimmed, rightExtra
|
||||
}
|
||||
Reference in New Issue
Block a user