docs(godoc): fix a bunch of typos

This commit is contained in:
Julien Neuhart
2025-04-17 11:15:52 +02:00
parent 9701f88ae3
commit 3cbf772acf
34 changed files with 123 additions and 122 deletions

View File

@@ -51,4 +51,4 @@ Head to the [documentation](https://gotenberg.dev/docs/getting-started/introduct
</a> </a>
</p> </p>
Sponsorships help maintaining and improving Gotenberg - [become a sponsor](https://github.com/sponsors/gulien) ❤️ Sponsorships help maintain and improve Gotenberg - [become a sponsor](https://github.com/sponsors/gulien) ❤️

View File

@@ -2,7 +2,7 @@
## Supported Versions ## Supported Versions
Please ensure to keep your environment up-to-date and use only the latest version of Gotenberg. Please ensure to keep your environment up to date and use only the latest version of Gotenberg.
Security updates and patches will be applied only to the most recent version. Security updates and patches will be applied only to the most recent version.
## Reporting a Vulnerability ## Reporting a Vulnerability

View File

@@ -74,7 +74,7 @@ func (cmd *Cmd) Start() error {
} }
// Wait waits for the command to complete. It should be called when using the // Wait waits for the command to complete. It should be called when using the
// Start method, so that the command does not leak zombies. // Start method so that the command does not leak zombies.
func (cmd *Cmd) Wait() error { func (cmd *Cmd) Wait() error {
err := cmd.process.Wait() err := cmd.process.Wait()
if err != nil { if err != nil {
@@ -84,7 +84,7 @@ func (cmd *Cmd) Wait() error {
return nil return nil
} }
// Exec executes the command and wait for its completion or until the context // Exec executes the command and waits for its completion or until the context
// is done. In any case, it kills the unix process and all its children. // is done. In any case, it kills the unix process and all its children.
func (cmd *Cmd) Exec() (int, error) { func (cmd *Cmd) Exec() (int, error) {
if cmd.ctx == nil { if cmd.ctx == nil {

View File

@@ -5,7 +5,7 @@ import (
"reflect" "reflect"
) )
// Context is a struct which helps to initialize modules. When provisioning, a // Context is a struct that helps to initialize modules. When provisioning, a
// module may use the context to get other modules that it needs internally. // module may use the context to get other modules that it needs internally.
type Context struct { type Context struct {
flags ParsedFlags flags ParsedFlags
@@ -58,7 +58,7 @@ func (ctx *Context) Module(kind interface{}) (interface{}, error) {
return mods[0], nil return mods[0], nil
} }
// Modules returns the list of modules which satisfies the requested interface. // Modules return the list of modules which satisfies the requested interface.
// //
// func (m *YourModule) Provision(ctx *gotenberg.Context) error { // func (m *YourModule) Provision(ctx *gotenberg.Context) error {
// mods, _ := ctx.Modules(new(ModuleInterface)) // mods, _ := ctx.Modules(new(ModuleInterface))

View File

@@ -23,7 +23,7 @@ func (o *OsMkdirAll) MkdirAll(path string, perm os.FileMode) error { return os.M
// PathRename defines the method signature for renaming files. Implement this // PathRename defines the method signature for renaming files. Implement this
// interface if you don't want to rely on [os.Rename], notably for testing // interface if you don't want to rely on [os.Rename], notably for testing
// purpose. // purposes.
type PathRename interface { type PathRename interface {
// Rename uses the same signature as [os.Rename]. // Rename uses the same signature as [os.Rename].
Rename(oldpath, newpath string) error Rename(oldpath, newpath string) error

View File

@@ -11,7 +11,7 @@ import (
) )
// GarbageCollect scans the root path and deletes files or directories with // GarbageCollect scans the root path and deletes files or directories with
// names containing specific substrings and before a given experiation time. // names containing specific substrings and before a given expiration time.
func GarbageCollect(logger *zap.Logger, rootPath string, includeSubstr []string, expirationTime time.Time) error { func GarbageCollect(logger *zap.Logger, rootPath string, includeSubstr []string, expirationTime time.Time) error {
logger = logger.Named("gc") logger = logger.Named("gc")

View File

@@ -28,29 +28,29 @@ func TestGarbageCollect(t *testing.T) {
{ {
scenario: "remove include substrings", scenario: "remove include substrings",
rootPath: func() string { rootPath: func() string {
path := fmt.Sprintf("%s/a_directory", os.TempDir()) p := fmt.Sprintf("%s/a_directory", os.TempDir())
err := os.MkdirAll(path, 0o755) err := os.MkdirAll(p, 0o755)
if err != nil { if err != nil {
t.Fatalf("expected no error but got: %v", err) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/a_foo_file", path), []byte{1}, 0o755) err = os.WriteFile(fmt.Sprintf("%s/a_foo_file", p), []byte{1}, 0o755)
if err != nil { if err != nil {
t.Fatalf("expected no error but got: %v", err) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/a_bar_file", path), []byte{1}, 0o755) err = os.WriteFile(fmt.Sprintf("%s/a_bar_file", p), []byte{1}, 0o755)
if err != nil { if err != nil {
t.Fatalf("expected no error but got: %v", err) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/a_baz_file", path), []byte{1}, 0o755) err = os.WriteFile(fmt.Sprintf("%s/a_baz_file", p), []byte{1}, 0o755)
if err != nil { if err != nil {
t.Fatalf("expected no error but got: %v", err) t.Fatalf("expected no error but got: %v", err)
} }
return path return p
}(), }(),
includeSubstr: []string{"foo", path.Join(os.TempDir(), "/a_directory/a_bar_file")}, includeSubstr: []string{"foo", path.Join(os.TempDir(), "/a_directory/a_bar_file")},
expectError: false, expectError: false,
@@ -81,18 +81,18 @@ func TestGarbageCollect(t *testing.T) {
} }
for _, name := range tc.expectNotExists { for _, name := range tc.expectNotExists {
path := fmt.Sprintf("%s/%s", tc.rootPath, name) p := fmt.Sprintf("%s/%s", tc.rootPath, name)
_, err = os.Stat(path) _, err = os.Stat(p)
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
t.Errorf("expected '%s' not to exist but it does: %v", path, err) t.Errorf("expected '%s' not to exist but it does: %v", p, err)
} }
} }
for _, name := range tc.expectExists { for _, name := range tc.expectExists {
path := fmt.Sprintf("%s/%s", tc.rootPath, name) p := fmt.Sprintf("%s/%s", tc.rootPath, name)
_, err = os.Stat(path) _, err = os.Stat(p)
if os.IsNotExist(err) { if os.IsNotExist(err) {
t.Errorf("expected '%s' to exist but it does not: %v", path, err) t.Errorf("expected '%s' to exist but it does not: %v", p, err)
} }
} }
}) })

View File

@@ -18,7 +18,7 @@ type LoggerProvider interface {
Logger(mod Module) (*zap.Logger, error) Logger(mod Module) (*zap.Logger, error)
} }
// LeveledLogger is wrapper around a [zap.Logger] so that it may be used by a // LeveledLogger is a wrapper around a [zap.Logger] so that it may be used by a
// [retryablehttp.Client]. // [retryablehttp.Client].
type LeveledLogger struct { type LeveledLogger struct {
logger *zap.Logger logger *zap.Logger
@@ -31,22 +31,22 @@ func NewLeveledLogger(logger *zap.Logger) *LeveledLogger {
} }
} }
// Error logs a message at error level using the wrapped zap.Logger. // Error logs a message at the error level using the wrapped zap.Logger.
func (leveled LeveledLogger) Error(msg string, keysAndValues ...interface{}) { func (leveled LeveledLogger) Error(msg string, keysAndValues ...interface{}) {
leveled.logger.Error(fmt.Sprintf("%s: %+v", msg, keysAndValues)) leveled.logger.Error(fmt.Sprintf("%s: %+v", msg, keysAndValues))
} }
// Warn logs a message at warning level using the wrapped zap.Logger. // Warn logs a message at the warning level using the wrapped zap.Logger.
func (leveled LeveledLogger) Warn(msg string, keysAndValues ...interface{}) { func (leveled LeveledLogger) Warn(msg string, keysAndValues ...interface{}) {
leveled.logger.Warn(fmt.Sprintf("%s: %+v", msg, keysAndValues)) leveled.logger.Warn(fmt.Sprintf("%s: %+v", msg, keysAndValues))
} }
// Info logs a message at info level using the wrapped zap.Logger. // Info logs a message at the info level using the wrapped zap.Logger.
func (leveled LeveledLogger) Info(msg string, keysAndValues ...interface{}) { func (leveled LeveledLogger) Info(msg string, keysAndValues ...interface{}) {
leveled.logger.Info(fmt.Sprintf("%s: %+v", msg, keysAndValues)) leveled.logger.Info(fmt.Sprintf("%s: %+v", msg, keysAndValues))
} }
// Debug logs a message at debug level using the wrapped zap.Logger. // Debug logs a message at the debug level using the wrapped zap.Logger.
func (leveled LeveledLogger) Debug(msg string, keysAndValues ...interface{}) { func (leveled LeveledLogger) Debug(msg string, keysAndValues ...interface{}) {
leveled.logger.Debug(fmt.Sprintf("%s: %+v", msg, keysAndValues)) leveled.logger.Debug(fmt.Sprintf("%s: %+v", msg, keysAndValues))
} }

View File

@@ -13,7 +13,7 @@ var (
ErrPdfEngineMethodNotSupported = errors.New("method not supported") ErrPdfEngineMethodNotSupported = errors.New("method not supported")
// ErrPdfSplitModeNotSupported is returned when the Split method of the // ErrPdfSplitModeNotSupported is returned when the Split method of the
// PdfEngine interface does not sumport a requested PDF split mode. // PdfEngine interface does not support a requested PDF split mode.
ErrPdfSplitModeNotSupported = errors.New("split mode not supported") ErrPdfSplitModeNotSupported = errors.New("split mode not supported")
// ErrPdfFormatNotSupported is returned when the Convert method of the // ErrPdfFormatNotSupported is returned when the Convert method of the
@@ -86,7 +86,7 @@ type PdfFormats struct {
} }
// PdfEngine provides an interface for operations on PDFs. Implementations // PdfEngine provides an interface for operations on PDFs. Implementations
// can utilize various tools like PDFtk, or implement functionality directly in // can use various tools like PDFtk, or implement functionality directly in
// Go. // Go.
// //
//nolint:dupl //nolint:dupl

View File

@@ -26,7 +26,7 @@ func init() {
gotenberg.MustRegisterModule(new(Api)) gotenberg.MustRegisterModule(new(Api))
} }
// Api is a module which provides an HTTP server. Other modules may add routes, // Api is a module that provides an HTTP server. Other modules may add routes,
// middlewares or health checks. // middlewares or health checks.
type Api struct { type Api struct {
port int port int
@@ -60,7 +60,7 @@ type downloadFromConfig struct {
disable bool disable bool
} }
// Router is a module interface which adds routes to the [Api]. // Router is a module interface that adds routes to the [Api].
type Router interface { type Router interface {
Routes() ([]Route, error) Routes() ([]Route, error)
} }
@@ -83,17 +83,17 @@ type Route struct {
// Optional. // Optional.
DisableLogging bool DisableLogging bool
// Handler is the function which handles the request. // Handler is the function that handles the request.
// Required. // Required.
Handler echo.HandlerFunc Handler echo.HandlerFunc
} }
// MiddlewareProvider is a module interface which adds middlewares to the [Api]. // MiddlewareProvider is a module interface that adds middlewares to the [Api].
type MiddlewareProvider interface { type MiddlewareProvider interface {
Middlewares() ([]Middleware, error) Middlewares() ([]Middleware, error)
} }
// MiddlewareStack is a type which helps to determine in which stack the // MiddlewareStack is a type that helps to determine in which stack the
// middlewares provided by the [MiddlewareProvider] modules should be located. // middlewares provided by the [MiddlewareProvider] modules should be located.
type MiddlewareStack uint32 type MiddlewareStack uint32
@@ -103,7 +103,7 @@ const (
MultipartStack MultipartStack
) )
// MiddlewarePriority is a type which helps to determine the execution order of // MiddlewarePriority is a type that helps to determine the execution order of
// middlewares provided by the [MiddlewareProvider] modules in a stack. // middlewares provided by the [MiddlewareProvider] modules in a stack.
type MiddlewarePriority uint32 type MiddlewarePriority uint32
@@ -115,7 +115,7 @@ const (
VeryHighPriority VeryHighPriority
) )
// Middleware is a middleware which can be added to the [Api]'s middlewares // Middleware is a middleware that can be added to the [Api]'s middlewares
// chain. // chain.
// //
// middleware := Middleware{ // middleware := Middleware{
@@ -157,7 +157,7 @@ type Middleware struct {
Handler echo.MiddlewareFunc Handler echo.MiddlewareFunc
} }
// HealthChecker is a module interface which allows adding health checks to the // HealthChecker is a module interface that allows adding health checks to the
// API. // API.
// //
// See https://github.com/alexliesenfeld/health for more details. // See https://github.com/alexliesenfeld/health for more details.
@@ -431,7 +431,7 @@ func (a *Api) Start() error {
} }
} }
// Check if the user wish to add logging entries related to the health // Check if the user wishes to add logging entries related to the health
// check route. // check route.
if a.disableHealthCheckLogging { if a.disableHealthCheckLogging {
disableLoggingForPaths = append(disableLoggingForPaths, "health") disableLoggingForPaths = append(disableLoggingForPaths, "health")

View File

@@ -1,3 +1,3 @@
// Package api provides a module which is an HTTP server. Other modules may // Package api provides a module, which is an HTTP server. Other modules may
// add multipart/form-data routes, middlewares, and health checks. // add multipart/form-data routes, middlewares, and health checks.
package api package api

View File

@@ -27,7 +27,7 @@ type FormData struct {
} }
// Validate returns nil or an error related to the [FormData] values, with a // Validate returns nil or an error related to the [FormData] values, with a
// [SentinelHttpError] (status code 400, errors' details as message) wrapped // [SentinelHttpError] (status code 400, errors' details as a message) wrapped
// inside. // inside.
// //
// var foo string // var foo string
@@ -96,7 +96,7 @@ func (form *FormData) Int(key string, target *int, defaultValue int) *FormData {
} }
// MandatoryInt binds a form field to an int variable. It populates an // MandatoryInt binds a form field to an int variable. It populates an
// error if the value is not int, is empty, or the "key" does not exist. // error if the value is not int, or is empty, or the "key" does not exist.
// //
// var foo int // var foo int
// //
@@ -116,7 +116,7 @@ func (form *FormData) Float64(key string, target *float64, defaultValue float64)
} }
// MandatoryFloat64 binds a form field to a float64 variable. It populates // MandatoryFloat64 binds a form field to a float64 variable. It populates
// an error if the is not float64, is empty, or the "key" does not exist. // an error if the value is not float64, is empty, or the "key" does not exist.
// //
// var foo float64 // var foo float64
// //
@@ -136,8 +136,8 @@ func (form *FormData) Duration(key string, target *time.Duration, defaultValue t
} }
// MandatoryDuration binds a form field to a time.Duration variable. It // MandatoryDuration binds a form field to a time.Duration variable. It
// populates an error if the value is not time.Duration, is empty, or the "key" // populates an error if the value is not time.Duration, or is empty, or the
// does not exist. // "key" does not exist.
// //
// var foo time.Duration // var foo time.Duration
// //
@@ -146,7 +146,7 @@ func (form *FormData) MandatoryDuration(key string, target *time.Duration) *Form
return form.mustMandatoryField(key, target) return form.mustMandatoryField(key, target)
} }
// Inches binds a form field to a float64 variable. It populates an error // Inches bind a form field to a float64 variable. It populates an error
// if the value cannot be computed back to inches. // if the value cannot be computed back to inches.
// //
// var foo float64 // var foo float64
@@ -303,7 +303,7 @@ func (form *FormData) Path(filename string, target *string) *FormData {
return form.path(filename, target) return form.path(filename, target)
} }
// MandatoryPath binds the absolute path ofa form data file to a string // MandatoryPath binds the absolute path of a form data file to a string
// variable. It populates an error if the file does not exist. // variable. It populates an error if the file does not exist.
// //
// var path string // var path string
@@ -348,7 +348,7 @@ func (form *FormData) MandatoryContent(filename string, target *string) *FormDat
return form.readFile(path, filename, target) return form.readFile(path, filename, target)
} }
// Paths binds the absolute paths of form data files, according to a list of // Paths bind the absolute paths of form data files, according to a list of
// file extensions, to a string slice variable. // file extensions, to a string slice variable.
// //
// var paths []string // var paths []string
@@ -379,7 +379,7 @@ func (form *FormData) MandatoryPaths(extensions []string, target *[]string) *For
return form return form
} }
// paths binds the absolute paths of form data files, according to a list of // paths bind the absolute paths of form data files, according to a list of
// file extensions, to a string slice variable. // file extensions, to a string slice variable.
func (form *FormData) paths(extensions []string, target *[]string) *FormData { func (form *FormData) paths(extensions []string, target *[]string) *FormData {
for filename, path := range form.files { for filename, path := range form.files {

View File

@@ -86,7 +86,7 @@ func httpErrorHandler() echo.HTTPErrorHandler {
} }
// latencyMiddleware sets the start time in the [echo.Context] under // latencyMiddleware sets the start time in the [echo.Context] under
// "startTime". Its value will be used later to calculate a request latency. // "startTime". Its value will be used later to calculate request latency.
// //
// startTime := c.Get("startTime").(time.Time) // startTime := c.Get("startTime").(time.Time)
func latencyMiddleware() echo.MiddlewareFunc { func latencyMiddleware() echo.MiddlewareFunc {
@@ -109,7 +109,7 @@ func latencyMiddleware() echo.MiddlewareFunc {
// rootPath := c.Get("rootPath").(string) // rootPath := c.Get("rootPath").(string)
// healthURI := fmt.Sprintf("%s/health", rootPath) // healthURI := fmt.Sprintf("%s/health", rootPath)
// //
// // Skip the middleware if health check URI. // // Skip the middleware if it's the health check URI.
// if c.Request().RequestURI == healthURI { // if c.Request().RequestURI == healthURI {
// // Call the next middleware in the chain. // // Call the next middleware in the chain.
// return next(c) // return next(c)
@@ -241,7 +241,7 @@ func basicAuthMiddleware(username, password string) echo.MiddlewareFunc {
}) })
} }
// contextMiddleware, a middleware for "multipart/form-data" requests, sets the // contextMiddleware, middleware for "multipart/form-data" requests, sets the
// [Context] and related context.CancelFunc in the [echo.Context] under // [Context] and related context.CancelFunc in the [echo.Context] under
// "context" and "cancel". If the process is synchronous, it also handles the // "context" and "cancel". If the process is synchronous, it also handles the
// result of a "multipart/form-data" request. // result of a "multipart/form-data" request.
@@ -256,7 +256,7 @@ func contextMiddleware(fs *gotenberg.FileSystem, timeout time.Duration, bodyLimi
trace := c.Get("trace").(string) trace := c.Get("trace").(string)
// We create a context with a timeout so that underlying processes are // We create a context with a timeout so that underlying processes are
// able to stop early and handle correctly a timeout scenario. // able to stop early and correctly handle a timeout scenario.
ctx, cancel, err := newContext(c, logger, fs, timeout, bodyLimit, downloadFromCfg, traceHeader, trace) ctx, cancel, err := newContext(c, logger, fs, timeout, bodyLimit, downloadFromCfg, traceHeader, trace)
if err != nil { if err != nil {
cancel() cancel()

View File

@@ -54,7 +54,7 @@ func (ctx *ContextMock) SetFiles(files map[string]string) {
ctx.files = files ctx.files = files
} }
// SetCancelled sets if the context is cancelled or not. // SetCancelled sets if the context is canceled or not.
// //
// ctx := &api.ContextMock{Context: &api.Context{}} // ctx := &api.ContextMock{Context: &api.Context{}}
// ctx.SetCancelled(true) // ctx.SetCancelled(true)
@@ -120,7 +120,7 @@ func (provider *MiddlewareProviderMock) Middlewares() ([]Middleware, error) {
return provider.MiddlewaresMock() return provider.MiddlewaresMock()
} }
// HealthCheckerMock is mock for the [HealthChecker] interface. // HealthCheckerMock is a mock for the [HealthChecker] interface.
type HealthCheckerMock struct { type HealthCheckerMock struct {
ChecksMock func() ([]health.CheckerOption, error) ChecksMock func() ([]health.CheckerOption, error)
ReadyMock func() error ReadyMock func() error

View File

@@ -211,7 +211,7 @@ func (b *chromiumBrowser) Stop(logger *zap.Logger) error {
logger.Debug(fmt.Sprintf("'%s' Chromium's user profile directory removed", userProfileDirPath)) logger.Debug(fmt.Sprintf("'%s' Chromium's user profile directory removed", userProfileDirPath))
} }
// Also remove Chromium specific files in the temporary directory. // Also, remove Chromium-specific files in the temporary directory.
err = gotenberg.GarbageCollect(logger, os.TempDir(), []string{".org.chromium.Chromium", ".com.google.Chrome"}, expirationTime) err = gotenberg.GarbageCollect(logger, os.TempDir(), []string{".org.chromium.Chromium", ".com.google.Chrome"}, expirationTime)
if err != nil { if err != nil {
logger.Error(err.Error()) logger.Error(err.Error())
@@ -314,7 +314,7 @@ func (b *chromiumBrowser) do(ctx context.Context, logger *zap.Logger, url string
return errors.New("context has no deadline") return errors.New("context has no deadline")
} }
// We validate the "main" URL against our allow / deny lists. // We validate the "main" URL against our allowed / deny lists.
err := gotenberg.FilterDeadline(b.arguments.allowList, b.arguments.denyList, url, deadline) err := gotenberg.FilterDeadline(b.arguments.allowList, b.arguments.denyList, url, deadline)
if err != nil { if err != nil {
return fmt.Errorf("filter URL: %w", err) return fmt.Errorf("filter URL: %w", err)
@@ -329,7 +329,7 @@ func (b *chromiumBrowser) do(ctx context.Context, logger *zap.Logger, url string
taskCtx, taskCancel := chromedp.NewContext(timeoutCtx) taskCtx, taskCancel := chromedp.NewContext(timeoutCtx)
defer taskCancel() defer taskCancel()
// We validate all others requests against our allow / deny lists. // We validate all other requests against our allowed / deny lists.
// If a request does not pass the validation, we make it fail. It also set // If a request does not pass the validation, we make it fail. It also set
// the extra HTTP headers, if any. // the extra HTTP headers, if any.
// See https://github.com/gotenberg/gotenberg/issues/1011. // See https://github.com/gotenberg/gotenberg/issues/1011.

View File

@@ -26,7 +26,7 @@ func init() {
var ( var (
// ErrInvalidEmulatedMediaType happens if the emulated media type is not // ErrInvalidEmulatedMediaType happens if the emulated media type is not
// "screen" nor "print". Empty value are allowed though. // "screen" nor "print". Empty value is allowed, though.
ErrInvalidEmulatedMediaType = errors.New("invalid emulated media type") ErrInvalidEmulatedMediaType = errors.New("invalid emulated media type")
// ErrInvalidEvaluationExpression happens if an evaluation expression // ErrInvalidEvaluationExpression happens if an evaluation expression
@@ -38,11 +38,11 @@ var (
ErrRpccMessageTooLarge = errors.New("rpcc message too large") ErrRpccMessageTooLarge = errors.New("rpcc message too large")
// ErrInvalidHttpStatusCode happens when the status code from the main page // ErrInvalidHttpStatusCode happens when the status code from the main page
// matches with one of the entry in [Options.FailOnHttpStatusCodes]. // matches with one of the entries in [Options.FailOnHttpStatusCodes].
ErrInvalidHttpStatusCode = errors.New("invalid HTTP status code") ErrInvalidHttpStatusCode = errors.New("invalid HTTP status code")
// ErrInvalidResourceHttpStatusCode happens when the status code from one // ErrInvalidResourceHttpStatusCode happens when the status code from one
// or more resources matches with one of the entry in // or more resources matches with one of the entries in
// [Options.FailOnResourceHttpStatusCodes]. // [Options.FailOnResourceHttpStatusCodes].
ErrInvalidResourceHttpStatusCode = errors.New("invalid resource HTTP status code") ErrInvalidResourceHttpStatusCode = errors.New("invalid resource HTTP status code")
@@ -68,11 +68,11 @@ var (
ErrInvalidPrinterSettings = errors.New("invalid printer settings") ErrInvalidPrinterSettings = errors.New("invalid printer settings")
// ErrPageRangesSyntaxError happens if the PdfOptions have an invalid page // ErrPageRangesSyntaxError happens if the PdfOptions have an invalid page
// ranges. // range.
ErrPageRangesSyntaxError = errors.New("page ranges syntax error") ErrPageRangesSyntaxError = errors.New("page ranges syntax error")
) )
// Chromium is a module which provides both an [Api] and routes for converting // Chromium is a module that provides both an [Api] and routes for converting
// HTML document to PDF. // HTML document to PDF.
type Chromium struct { type Chromium struct {
autoStart bool autoStart bool
@@ -128,15 +128,15 @@ type Options struct {
UserAgent string UserAgent string
// ExtraHttpHeaders are extra HTTP headers to send by Chromium while // ExtraHttpHeaders are extra HTTP headers to send by Chromium while
// loading he HTML document. // loading the HTML document.
ExtraHttpHeaders []ExtraHttpHeader ExtraHttpHeaders []ExtraHttpHeader
// EmulatedMediaType is the media type to emulate, either "screen" or // EmulatedMediaType is the media type to emulate, either "screen" or
// "print". // "print".
EmulatedMediaType string EmulatedMediaType string
// OmitBackground hides default white background and allows generating PDFs // OmitBackground hides the default white background and allows generating
// with transparency. // PDFs with transparency.
OmitBackground bool OmitBackground bool
} }
@@ -197,9 +197,9 @@ type PdfOptions struct {
// Page ranges to print, e.g., '1-5, 8, 11-13'. Empty means all pages. // Page ranges to print, e.g., '1-5, 8, 11-13'. Empty means all pages.
PageRanges string PageRanges string
// HeaderTemplate is the HTML template of the header. It should be valid // HeaderTemplate is the HTML template of the header. It should be a valid
// HTML markup with following classes used to inject printing values into // HTML markup with the following classes used to inject printing values
// them: // into them:
// - date: formatted print date // - date: formatted print date
// - title: document title // - title: document title
// - url: document location // - url: document location
@@ -338,7 +338,7 @@ type Api interface {
Screenshot(ctx context.Context, logger *zap.Logger, url, outputPath string, options ScreenshotOptions) error Screenshot(ctx context.Context, logger *zap.Logger, url, outputPath string, options ScreenshotOptions) error
} }
// Provider is a module interface which exposes a method for creating an [Api] // Provider is a module interface that exposes a method for creating an [Api]
// for other modules. // for other modules.
// //
// func (m *YourModule) Provision(ctx *gotenberg.Context) error { // func (m *YourModule) Provision(ctx *gotenberg.Context) error {
@@ -473,8 +473,8 @@ func (mod *Chromium) StartupMessage() string {
// Stop stops the current browser instance. // Stop stops the current browser instance.
func (mod *Chromium) Stop(ctx context.Context) error { func (mod *Chromium) Stop(ctx context.Context) error {
// Block until the context is done so that other module may gracefully stop // Block until the context is done so that another module may gracefully
// before we do a shutdown. // stop before we do a shutdown.
mod.logger.Debug("wait for the end of grace duration") mod.logger.Debug("wait for the end of grace duration")
<-ctx.Done() <-ctx.Done()

View File

@@ -72,10 +72,10 @@ func listenForEventRequestPaused(ctx context.Context, logger *zap.Logger, option
var extraHttpHeadersToSet []ExtraHttpHeader var extraHttpHeadersToSet []ExtraHttpHeader
if len(options.extraHttpHeaders) > 0 { if len(options.extraHttpHeaders) > 0 {
// The user want to set extra HTTP headers. // The user wants to set extra HTTP headers.
// First, we have to check if at least one header has to be // First, we have to check if at least one header has to be
// set for current request. // set for the current request.
for _, header := range options.extraHttpHeaders { for _, header := range options.extraHttpHeaders {
if header.Scope == nil { if header.Scope == nil {
// Non-scoped header. // Non-scoped header.
@@ -147,8 +147,8 @@ type eventResponseReceivedOptions struct {
invalidResourceHttpStatusCodeMu *sync.RWMutex invalidResourceHttpStatusCodeMu *sync.RWMutex
} }
// listenForEventResponseReceived listens for an invalid HTTP status code that // listenForEventResponseReceived listens for an invalid HTTP status code
// is returned by the main page or by one or more resources. // returned by the main page or by one or more resources.
// See: // See:
// https://github.com/gotenberg/gotenberg/issues/613. // https://github.com/gotenberg/gotenberg/issues/613.
// https://github.com/gotenberg/gotenberg/issues/1021. // https://github.com/gotenberg/gotenberg/issues/1021.

View File

@@ -25,7 +25,7 @@ import (
) )
// FormDataChromiumOptions creates [Options] from the form data. Fallback to // FormDataChromiumOptions creates [Options] from the form data. Fallback to
// default value if the considered key is not present. // the default value if the considered key is not present.
func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) { func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
defaultOptions := DefaultOptions() defaultOptions := DefaultOptions()
@@ -194,7 +194,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
} }
// FormDataChromiumPdfOptions creates [PdfOptions] from the form data. Fallback to // FormDataChromiumPdfOptions creates [PdfOptions] from the form data. Fallback to
// default value if the considered key is not present. // the default value if the considered key is not present.
func FormDataChromiumPdfOptions(ctx *api.Context) (*api.FormData, PdfOptions) { func FormDataChromiumPdfOptions(ctx *api.Context) (*api.FormData, PdfOptions) {
form, options := FormDataChromiumOptions(ctx) form, options := FormDataChromiumOptions(ctx)
defaultPdfOptions := DefaultPdfOptions() defaultPdfOptions := DefaultPdfOptions()
@@ -249,7 +249,7 @@ func FormDataChromiumPdfOptions(ctx *api.Context) (*api.FormData, PdfOptions) {
} }
// FormDataChromiumScreenshotOptions creates [ScreenshotOptions] from the form // FormDataChromiumScreenshotOptions creates [ScreenshotOptions] from the form
// data. Fallback to default value if the considered key is not present. // data. Fallback to the default value if the considered key is not present.
func FormDataChromiumScreenshotOptions(ctx *api.Context) (*api.FormData, ScreenshotOptions) { func FormDataChromiumScreenshotOptions(ctx *api.Context) (*api.FormData, ScreenshotOptions) {
form, options := FormDataChromiumOptions(ctx) form, options := FormDataChromiumOptions(ctx)
defaultScreenshotOptions := DefaultScreenshotOptions() defaultScreenshotOptions := DefaultScreenshotOptions()
@@ -483,7 +483,7 @@ func convertMarkdownRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
} }
// screenshotMarkdownRoute returns an [api.Route] which can take a screenshot // screenshotMarkdownRoute returns an [api.Route] which can take a screenshot
// from markdown files. // from Markdown files.
func screenshotMarkdownRoute(chromium Api) api.Route { func screenshotMarkdownRoute(chromium Api) api.Route {
return api.Route{ return api.Route{
Method: http.MethodPost, Method: http.MethodPost,
@@ -522,7 +522,7 @@ func screenshotMarkdownRoute(chromium Api) api.Route {
} }
func markdownToHtml(ctx *api.Context, inputPath string, markdownPaths []string) (string, error) { func markdownToHtml(ctx *api.Context, inputPath string, markdownPaths []string) (string, error) {
// We have to convert each markdown file referenced in the HTML // We have to convert each Markdown file referenced in the HTML
// file to... HTML. Thanks to the "html/template" package, we are // file to... HTML. Thanks to the "html/template" package, we are
// able to provide the "toHTML" function which the user may call // able to provide the "toHTML" function which the user may call
// directly inside the HTML file. // directly inside the HTML file.

View File

@@ -24,7 +24,7 @@ type streamReader struct {
// Read a chunk of the stream. // Read a chunk of the stream.
func (reader *streamReader) Read(p []byte) (n int, err error) { func (reader *streamReader) Read(p []byte) (n int, err error) {
if reader.r != nil { if reader.r != nil {
// Continue reading from buffer. // Continue reading from the buffer.
return reader.read(p) return reader.read(p)
} }

View File

@@ -142,15 +142,15 @@ func (engine *ExifTool) WriteMetadata(ctx context.Context, logger *zap.Logger, m
fileMetadata[0].SetStrings(key, val) fileMetadata[0].SetStrings(key, val)
case []interface{}: case []interface{}:
// See https://github.com/gotenberg/gotenberg/issues/1048. // See https://github.com/gotenberg/gotenberg/issues/1048.
strings := make([]string, len(val)) strs := make([]string, len(val))
for i, entry := range val { for i, entry := range val {
if str, ok := entry.(string); ok { if str, ok := entry.(string); ok {
strings[i] = str strs[i] = str
continue continue
} }
return fmt.Errorf("write PDF metadata with ExifTool: %s %+v %s %w", key, val, reflect.TypeOf(val), gotenberg.ErrPdfEngineMetadataValueNotSupported) return fmt.Errorf("write PDF metadata with ExifTool: %s %+v %s %w", key, val, reflect.TypeOf(val), gotenberg.ErrPdfEngineMetadataValueNotSupported)
} }
fileMetadata[0].SetStrings(key, strings) fileMetadata[0].SetStrings(key, strs)
case bool: case bool:
fileMetadata[0].SetString(key, fmt.Sprintf("%t", val)) fileMetadata[0].SetString(key, fmt.Sprintf("%t", val))
case int: case int:

View File

@@ -24,23 +24,23 @@ func init() {
} }
var ( var (
// ErrInvalidPdfFormats happens if the PDF formats option cannot be handled // ErrInvalidPdfFormats happens if LibreOffice cannot handle the PDF
// by LibreOffice. // formats option.
ErrInvalidPdfFormats = errors.New("invalid PDF formats") ErrInvalidPdfFormats = errors.New("invalid PDF formats")
// ErrUnoException happens when unoconverter returns an exit code 5. // ErrUnoException happens when unoconverter returns exit code 5.
ErrUnoException = errors.New("uno exception") ErrUnoException = errors.New("uno exception")
// ErrRuntimeException happens when unoconverter returns an exit code 6. // ErrRuntimeException happens when unoconverter returns exit code 6.
ErrRuntimeException = errors.New("uno exception") ErrRuntimeException = errors.New("uno exception")
// ErrCoreDumped happens randomly; sometime a conversion will work as // ErrCoreDumped happens randomly; sometimes a conversion will work as
// expected, and some other time the same conversion will fail. // expected, and some other time the same conversion will fail.
// See https://github.com/gotenberg/gotenberg/issues/639. // See https://github.com/gotenberg/gotenberg/issues/639.
ErrCoreDumped = errors.New("core dumped") ErrCoreDumped = errors.New("core dumped")
) )
// Api is a module which provides a [Uno] to interact with LibreOffice. // Api is a module that provides a [Uno] to interact with LibreOffice.
type Api struct { type Api struct {
autoStart bool autoStart bool
args libreOfficeArguments args libreOfficeArguments
@@ -56,10 +56,10 @@ type Options struct {
// Password specifies the password for opening the source file. // Password specifies the password for opening the source file.
Password string Password string
// Landscape allows to change the orientation of the resulting PDF. // Landscape allows changing the orientation of the resulting PDF.
Landscape bool Landscape bool
// PageRanges allows to select the pages to convert. // PageRanges allows selecting the pages to convert.
PageRanges string PageRanges string
// UpdateIndexes specifies whether to update the indexes before conversion, // UpdateIndexes specifies whether to update the indexes before conversion,
@@ -83,7 +83,7 @@ type Options struct {
// Named Destination. // Named Destination.
ExportBookmarksToPdfDestination bool ExportBookmarksToPdfDestination bool
// ExportPlaceholders exports the placeholders fields visual markings only. // ExportPlaceholders exports the placeholder fields visual markings only.
// The exported placeholder is ineffective. // The exported placeholder is ineffective.
ExportPlaceholders bool ExportPlaceholders bool
@@ -94,15 +94,16 @@ type Options struct {
// Notes pages are available in Impress documents only. // Notes pages are available in Impress documents only.
ExportNotesPages bool ExportNotesPages bool
// ExportOnlyNotesPages specifies, if the property ExportNotesPages is set // ExportOnlyNotesPages specifies if the property ExportNotesPages is set
// to true, if only notes pages are exported to PDF. // to true if only notes pages are exported to PDF.
ExportOnlyNotesPages bool ExportOnlyNotesPages bool
// ExportNotesInMargin specifies if notes in margin are exported to PDF. // ExportNotesInMargin specifies if notes in the margin are exported to
// PDF.
ExportNotesInMargin bool ExportNotesInMargin bool
// ConvertOooTargetToPdfTarget specifies that the target documents with // ConvertOooTargetToPdfTarget specifies that the target documents with
// .od[tpgs] extension, will have that extension changed to .pdf when the // .od[tpgs] extension will have that extension changed to .pdf when the
// link is exported to PDF. The source document remains untouched. // link is exported to PDF. The source document remains untouched.
ConvertOooTargetToPdfTarget bool ConvertOooTargetToPdfTarget bool
@@ -190,7 +191,7 @@ type Uno interface {
Extensions() []string Extensions() []string
} }
// Provider is a module interface which exposes a method for creating a // Provider is a module interface that exposes a method for creating a
// [Uno] for other modules. // [Uno] for other modules.
// //
// func (m *YourModule) Provision(ctx *gotenberg.Context) error { // func (m *YourModule) Provision(ctx *gotenberg.Context) error {
@@ -300,8 +301,8 @@ func (a *Api) StartupMessage() string {
// Stop stops the current browser instance. // Stop stops the current browser instance.
func (a *Api) Stop(ctx context.Context) error { func (a *Api) Stop(ctx context.Context) error {
// Block until the context is done so that other module may gracefully stop // Block until the context is done so that another module may gracefully
// before we do a shutdown. // stop before we do a shutdown.
a.logger.Debug("wait for the end of grace duration") a.logger.Debug("wait for the end of grace duration")
<-ctx.Done() <-ctx.Done()

View File

@@ -200,7 +200,7 @@ func (p *libreOfficeProcess) Stop(logger *zap.Logger) error {
logger.Debug(fmt.Sprintf("'%s' LibreOffice's user profile directory removed", userProfileDirPath)) logger.Debug(fmt.Sprintf("'%s' LibreOffice's user profile directory removed", userProfileDirPath))
} }
// Also remove LibreOffice specific files in the temporary directory. // Also, remove LibreOffice specific files in the temporary directory.
err = gotenberg.GarbageCollect(logger, os.TempDir(), []string{"OSL_PIPE", ".tmp"}, expirationTime) err = gotenberg.GarbageCollect(logger, os.TempDir(), []string{"OSL_PIPE", ".tmp"}, expirationTime)
if err != nil { if err != nil {
logger.Error(err.Error()) logger.Error(err.Error())
@@ -353,10 +353,10 @@ func (p *libreOfficeProcess) pdf(ctx context.Context, logger *zap.Logger, inputP
} }
// LibreOffice's errors are not explicit. // LibreOffice's errors are not explicit.
// For instance, an exit code 5 may be explained by a malformed page // For instance, exit code 5 may be explained by a malformed page range
// ranges, but also by a not required password. // but also by a not required password.
// We may want to retry in case of a core dumped event. // We may want to retry in case of a core-dumped event.
// See https://github.com/gotenberg/gotenberg/issues/639. // See https://github.com/gotenberg/gotenberg/issues/639.
if strings.Contains(err.Error(), "core dumped") { if strings.Contains(err.Error(), "core dumped") {
return ErrCoreDumped return ErrCoreDumped

View File

@@ -14,7 +14,7 @@ func init() {
gotenberg.MustRegisterModule(new(LibreOffice)) gotenberg.MustRegisterModule(new(LibreOffice))
} }
// LibreOffice is a module which provides a route for converting documents to // LibreOffice is a module that provides a route for converting documents to
// PDF with LibreOffice. // PDF with LibreOffice.
type LibreOffice struct { type LibreOffice struct {
api libeofficeapi.Uno api libeofficeapi.Uno

View File

@@ -58,7 +58,7 @@ func (engine *LibreOfficePdfEngine) Split(ctx context.Context, logger *zap.Logge
// Flatten is not available in this implementation. // Flatten is not available in this implementation.
func (engine *LibreOfficePdfEngine) Flatten(ctx context.Context, logger *zap.Logger, inputPath string) error { func (engine *LibreOfficePdfEngine) Flatten(ctx context.Context, logger *zap.Logger, inputPath string) error {
return fmt.Errorf("Flatten PDF with LibreOffice: %w", gotenberg.ErrPdfEngineMethodNotSupported) return fmt.Errorf("flatten PDF with LibreOffice: %w", gotenberg.ErrPdfEngineMethodNotSupported)
} }
// Convert converts the given PDF to a specific PDF format. Currently, only the // Convert converts the given PDF to a specific PDF format. Currently, only the

View File

@@ -164,7 +164,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
} }
if nativePdfFormats && splitMode == zeroValuedSplitMode { if nativePdfFormats && splitMode == zeroValuedSplitMode {
// Only apply natively given PDF formats if we're not // Only natively apply given PDF formats if we're not
// splitting the PDF later. // splitting the PDF later.
options.PdfFormats = pdfFormats options.PdfFormats = pdfFormats
} }

View File

@@ -31,7 +31,7 @@ const (
textLoggingFormat = "text" textLoggingFormat = "text"
) )
// Logging is a module which implements the [gotenberg.LoggerProvider] // Logging is a module that implements the [gotenberg.LoggerProvider]
// interface. // interface.
type Logging struct { type Logging struct {
level string level string

View File

@@ -124,7 +124,7 @@ func (multi *multiPdfEngines) Flatten(ctx context.Context, logger *zap.Logger, i
return fmt.Errorf("flatten PDF with multi PDF engines: %w", err) return fmt.Errorf("flatten PDF with multi PDF engines: %w", err)
} }
// Convert converts the given PDF to a specific PDF format. thanks to its // Convert converts the given PDF to a specific PDF format, thanks to its
// children. If the context is done, it stops and returns an error. // children. If the context is done, it stops and returns an error.
func (multi *multiPdfEngines) Convert(ctx context.Context, logger *zap.Logger, formats gotenberg.PdfFormats, inputPath, outputPath string) error { func (multi *multiPdfEngines) Convert(ctx context.Context, logger *zap.Logger, formats gotenberg.PdfFormats, inputPath, outputPath string) error {
var err error var err error

View File

@@ -92,7 +92,7 @@ func (mod *PdfEngines) Provision(ctx *gotenberg.Context) error {
defaultNames[i] = engine.(gotenberg.Module).Descriptor().ID defaultNames[i] = engine.(gotenberg.Module).Descriptor().ID
} }
// Example in case of deprecated module name. // Example in the case of deprecated module name.
//for i, name := range defaultNames { //for i, name := range defaultNames {
// if name == "unoconv-pdfengine" || name == "uno-pdfengine" { // if name == "unoconv-pdfengine" || name == "uno-pdfengine" {
// logger.Warn(fmt.Sprintf("%s is deprecated; prefer libreoffice-pdfengine instead", name)) // logger.Warn(fmt.Sprintf("%s is deprecated; prefer libreoffice-pdfengine instead", name))

View File

@@ -33,7 +33,7 @@ func (engine *PdfTk) Descriptor() gotenberg.ModuleDescriptor {
} }
} }
// Provision sets the modules properties. // Provision sets the module properties.
func (engine *PdfTk) Provision(ctx *gotenberg.Context) error { func (engine *PdfTk) Provision(ctx *gotenberg.Context) error {
binPath, ok := os.LookupEnv("PDFTK_BIN_PATH") binPath, ok := os.LookupEnv("PDFTK_BIN_PATH")
if !ok { if !ok {

View File

@@ -20,7 +20,7 @@ func init() {
gotenberg.MustRegisterModule(new(Prometheus)) gotenberg.MustRegisterModule(new(Prometheus))
} }
// Prometheus is a module which collects metrics and exposes them via an HTTP // Prometheus is a module that collects metrics and exposes them via an HTTP
// route. // route.
type Prometheus struct { type Prometheus struct {
namespace string namespace string
@@ -49,7 +49,7 @@ func (mod *Prometheus) Descriptor() gotenberg.ModuleDescriptor {
} }
} }
// Provision sets the modules properties. // Provision sets the module properties.
func (mod *Prometheus) Provision(ctx *gotenberg.Context) error { func (mod *Prometheus) Provision(ctx *gotenberg.Context) error {
flags := ctx.ParsedFlags() flags := ctx.ParsedFlags()
mod.namespace = flags.MustString("prometheus-namespace") mod.namespace = flags.MustString("prometheus-namespace")

View File

@@ -34,7 +34,7 @@ func (engine *QPdf) Descriptor() gotenberg.ModuleDescriptor {
} }
} }
// Provision sets the modules properties. // Provision sets the module properties.
func (engine *QPdf) Provision(ctx *gotenberg.Context) error { func (engine *QPdf) Provision(ctx *gotenberg.Context) error {
binPath, ok := os.LookupEnv("QPDF_BIN_PATH") binPath, ok := os.LookupEnv("QPDF_BIN_PATH")
if !ok { if !ok {

View File

@@ -26,14 +26,14 @@ type client struct {
} }
// send call the webhook either to send the success response or the error response. // send call the webhook either to send the success response or the error response.
func (c client) send(body io.Reader, headers map[string]string, erroed bool) error { func (c client) send(body io.Reader, headers map[string]string, errored bool) error {
url := c.url url := c.url
if erroed { if errored {
url = c.errorUrl url = c.errorUrl
} }
method := c.method method := c.method
if erroed { if errored {
method = c.errorMethod method = c.errorMethod
} }
@@ -54,9 +54,9 @@ func (c client) send(body io.Reader, headers map[string]string, erroed bool) err
contentLength, ok := headers[echo.HeaderContentLength] contentLength, ok := headers[echo.HeaderContentLength]
if ok { if ok {
// Golang "http" package should automatically calculate the size of the // Golang "http" package should automatically calculate the size of the
// body. But, when using a buffered file reader, it does not work. // body. But when using a buffered file reader, it does not work.
// Worse, the "Content-Length" header is also removed. Therefore, in // Worse, the "Content-Length" header is also removed. Therefore,
// order to keep this valuable information, we have to trust the caller // to keep this valuable information, we have to trust the caller
// by reading the value of the "Content-Length" entry and set it as the // by reading the value of the "Content-Length" entry and set it as the
// content length of the request. It's kinda suboptimal, but hey, at // content length of the request. It's kinda suboptimal, but hey, at
// least it works. // least it works.
@@ -100,7 +100,7 @@ func (c client) send(body io.Reader, headers map[string]string, erroed bool) err
fields[3] = zap.String("latency_human", finishTime.Sub(c.startTime).String()) fields[3] = zap.String("latency_human", finishTime.Sub(c.startTime).String())
fields[4] = zap.Int64("bytes_out", req.ContentLength) fields[4] = zap.Int64("bytes_out", req.ContentLength)
if erroed { if errored {
c.logger.Warn("request to webhook with error details handled", fields...) c.logger.Warn("request to webhook with error details handled", fields...)
return nil return nil

View File

@@ -113,7 +113,7 @@ func webhookMiddleware(w *Webhook) api.Middleware {
} }
} }
// Retrieve values from echo.Context before it get recycled. // Retrieve values from echo.Context before it gets recycled.
// See https://github.com/gotenberg/gotenberg/issues/1000. // See https://github.com/gotenberg/gotenberg/issues/1000.
startTime := c.Get("startTime").(time.Time) startTime := c.Get("startTime").(time.Time)
traceHeader := c.Get("traceHeader").(string) traceHeader := c.Get("traceHeader").(string)
@@ -189,7 +189,7 @@ func webhookMiddleware(w *Webhook) api.Middleware {
return return
} }
// No error, let's get build the output file. // No error, let's get to build the output file.
outputPath, err := ctx.BuildOutputFile() outputPath, err := ctx.BuildOutputFile()
if err != nil { if err != nil {
ctx.Log().Error(fmt.Sprintf("build output file: %s", err)) ctx.Log().Error(fmt.Sprintf("build output file: %s", err))

View File

@@ -14,7 +14,7 @@ func init() {
gotenberg.MustRegisterModule(new(Webhook)) gotenberg.MustRegisterModule(new(Webhook))
} }
// Webhook is a module which provides a middleware for uploading output files // Webhook is a module that provides a middleware for uploading output files
// to any destinations in an asynchronous fashion. // to any destinations in an asynchronous fashion.
type Webhook struct { type Webhook struct {
allowList *regexp2.Regexp allowList *regexp2.Regexp