core

package
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 11, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package core provides core service logic and interfaces.

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidPath = errors.New("invalid path: directory traversal not allowed")

ErrInvalidPath is returned by store implementations when a document or asset path is empty, absolute, or attempts directory traversal. API handlers check this sentinel to return HTTP 400.

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound is returned by store implementations when a requested document or asset does not exist. API handlers check this sentinel to return HTTP 404.

Functions

func RewriteImageURLs

func RewriteImageURLs(html []byte, repo, docPath string) []byte

RewriteImageURLs rewrites relative image URLs in rendered HTML so they point to the asset serving route (/assets/{repo}/{resolvedPath}). Absolute URLs (http, https, //, data:) and already-absolute paths (starting with /) are left unchanged.

The query string and fragment of the original src are preserved: only the path component is rewritten so that references like "sprite.svg#icon" or "img.png?raw=1" keep their semantics after rewriting.

Types

type ContentProcessor

type ContentProcessor interface {
	// RenderHTML converts raw content into bytes consumed by the view layer
	// (e.g., HTML for markdown, JSON for OpenAPI) and returns any extracted headings.
	RenderHTML(src []byte) ([]byte, []Heading, error)
	// ExtractTitle returns a human-readable title from the content.
	ExtractTitle(src []byte) string
	// ToPlainText converts content to plain text for search indexing.
	ToPlainText(src []byte) string
	// ExtractHeadings returns the H1-H3 headings from the content with their
	// anchor IDs, used to resolve search result deep-links. Returns nil when
	// the content type does not support heading-based navigation.
	ExtractHeadings(src []byte) []Heading
}

ContentProcessor handles rendering and indexing for a specific content type.

type ContentType

type ContentType string

ContentType identifies the format of a document's content.

const (
	// ContentTypeMarkdown represents standard markdown documents.
	ContentTypeMarkdown ContentType = "markdown"
	// ContentTypeOpenAPI represents OpenAPI specification documents.
	ContentTypeOpenAPI ContentType = "openapi"
)

func DetectContentType

func DetectContentType(path string, content []byte) ContentType

DetectContentType determines the content type of a document based on its file path and content. It uses file extension as a fast pre-filter and then inspects the content for OpenAPI-specific markers (the "openapi" or "swagger" top-level keys). Files with non-YAML/JSON extensions are treated as markdown. YAML/JSON files that do not match OpenAPI heuristics return an empty ContentType to signal that they should be skipped (not treated as documentation).

type Document

type Document struct {
	UpdatedAt   time.Time
	ID          string
	Repo        string
	Path        string
	Title       string
	Content     string
	CommitSHA   string
	ContentType ContentType
}

Document represents a documentation file from a repository.

type DocumentMeta

type DocumentMeta struct {
	UpdatedAt   time.Time
	ID          string
	Repo        string
	Path        string
	Title       string
	ContentType ContentType
}

DocumentMeta contains metadata about a document without its full content.

type Heading

type Heading struct {
	ID    string
	Text  string
	Level int
}

Heading represents a heading extracted from a document for table of contents navigation.

type IngestAsset

type IngestAsset struct {
	Path    string `json:"path"`    // relative path from docs root (e.g., "images/arch.png")
	Content string `json:"content"` // base64-encoded binary content
	Action  string `json:"action"`  // "upsert" or "delete"
}

IngestAsset represents a binary asset (image, diagram, etc.) in an ingest request. Content is base64-encoded to allow transport within JSON payloads.

type IngestDocument

type IngestDocument struct {
	Path        string      `json:"path"`
	Content     string      `json:"content,omitempty"`
	Action      string      `json:"action"`                 // "upsert" or "delete"
	ContentType ContentType `json:"content_type,omitempty"` // defaults to "markdown" when empty
}

IngestDocument represents a single document in an ingest request.

type IngestRequest

type IngestRequest struct {
	Assets    *[]IngestAsset   `json:"assets,omitempty"`
	Repo      string           `json:"repo"`
	CommitSHA string           `json:"commit_sha"`
	Documents []IngestDocument `json:"documents"`
	Sync      bool             `json:"sync,omitempty"`
}

IngestRequest represents a batch document ingest request from a GitHub Action.

Assets uses a pointer-to-slice so the server can distinguish between an older client that omits the field entirely (nil pointer → skip stale-asset cleanup) and a newer client that explicitly sends an empty list (non-nil pointer with length zero → run cleanup, which will delete all stored assets for the repo).

type IngestResponse

type IngestResponse struct {
	Indexed       int `json:"indexed"`
	Deleted       int `json:"deleted"`
	AssetsStored  int `json:"assets_stored,omitempty"`
	AssetsDeleted int `json:"assets_deleted,omitempty"`
}

IngestResponse is returned after processing an ingest request.

type MockContentProcessor

type MockContentProcessor struct {
	mock.Mock
}

MockContentProcessor is an autogenerated mock type for the ContentProcessor type

func NewMockContentProcessor

func NewMockContentProcessor(t interface {
	mock.TestingT
	Cleanup(func())
}) *MockContentProcessor

NewMockContentProcessor creates a new instance of MockContentProcessor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. The first argument is typically a *testing.T value.

func (*MockContentProcessor) EXPECT

func (*MockContentProcessor) ExtractHeadings

func (_m *MockContentProcessor) ExtractHeadings(src []byte) []Heading

ExtractHeadings provides a mock function with given fields: src

func (*MockContentProcessor) ExtractTitle

func (_m *MockContentProcessor) ExtractTitle(src []byte) string

ExtractTitle provides a mock function with given fields: src

func (*MockContentProcessor) RenderHTML

func (_m *MockContentProcessor) RenderHTML(src []byte) ([]byte, []Heading, error)

RenderHTML provides a mock function with given fields: src

func (*MockContentProcessor) ToPlainText

func (_m *MockContentProcessor) ToPlainText(src []byte) string

ToPlainText provides a mock function with given fields: src

type MockContentProcessor_Expecter

type MockContentProcessor_Expecter struct {
	// contains filtered or unexported fields
}

func (*MockContentProcessor_Expecter) ExtractHeadings

func (_e *MockContentProcessor_Expecter) ExtractHeadings(src interface{}) *MockContentProcessor_ExtractHeadings_Call

ExtractHeadings is a helper method to define mock.On call

  • src []byte

func (*MockContentProcessor_Expecter) ExtractTitle

func (_e *MockContentProcessor_Expecter) ExtractTitle(src interface{}) *MockContentProcessor_ExtractTitle_Call

ExtractTitle is a helper method to define mock.On call

  • src []byte

func (*MockContentProcessor_Expecter) RenderHTML

RenderHTML is a helper method to define mock.On call

  • src []byte

func (*MockContentProcessor_Expecter) ToPlainText

func (_e *MockContentProcessor_Expecter) ToPlainText(src interface{}) *MockContentProcessor_ToPlainText_Call

ToPlainText is a helper method to define mock.On call

  • src []byte

type MockContentProcessor_ExtractHeadings_Call

type MockContentProcessor_ExtractHeadings_Call struct {
	*mock.Call
}

MockContentProcessor_ExtractHeadings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ExtractHeadings'

func (*MockContentProcessor_ExtractHeadings_Call) Return

func (*MockContentProcessor_ExtractHeadings_Call) Run

func (*MockContentProcessor_ExtractHeadings_Call) RunAndReturn

type MockContentProcessor_ExtractTitle_Call

type MockContentProcessor_ExtractTitle_Call struct {
	*mock.Call
}

MockContentProcessor_ExtractTitle_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ExtractTitle'

func (*MockContentProcessor_ExtractTitle_Call) Return

func (*MockContentProcessor_ExtractTitle_Call) Run

func (*MockContentProcessor_ExtractTitle_Call) RunAndReturn

type MockContentProcessor_RenderHTML_Call

type MockContentProcessor_RenderHTML_Call struct {
	*mock.Call
}

MockContentProcessor_RenderHTML_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RenderHTML'

func (*MockContentProcessor_RenderHTML_Call) Return

func (*MockContentProcessor_RenderHTML_Call) Run

func (*MockContentProcessor_RenderHTML_Call) RunAndReturn

type MockContentProcessor_ToPlainText_Call

type MockContentProcessor_ToPlainText_Call struct {
	*mock.Call
}

MockContentProcessor_ToPlainText_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ToPlainText'

func (*MockContentProcessor_ToPlainText_Call) Return

func (*MockContentProcessor_ToPlainText_Call) Run

func (*MockContentProcessor_ToPlainText_Call) RunAndReturn

type MockdocStore

type MockdocStore struct {
	mock.Mock
}

MockdocStore is an autogenerated mock type for the docStore type

func NewMockdocStore

func NewMockdocStore(t interface {
	mock.TestingT
	Cleanup(func())
}) *MockdocStore

NewMockdocStore creates a new instance of MockdocStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. The first argument is typically a *testing.T value.

func (*MockdocStore) Delete

func (_m *MockdocStore) Delete(ctx context.Context, repo string, path string) error

Delete provides a mock function with given fields: ctx, repo, path

func (*MockdocStore) DeleteAsset

func (_m *MockdocStore) DeleteAsset(ctx context.Context, repo string, path string) error

DeleteAsset provides a mock function with given fields: ctx, repo, path

func (*MockdocStore) EXPECT

func (_m *MockdocStore) EXPECT() *MockdocStore_Expecter

func (*MockdocStore) Get

func (_m *MockdocStore) Get(ctx context.Context, repo string, path string) (Document, error)

Get provides a mock function with given fields: ctx, repo, path

func (*MockdocStore) GetAsset

func (_m *MockdocStore) GetAsset(ctx context.Context, repo string, path string) ([]byte, error)

GetAsset provides a mock function with given fields: ctx, repo, path

func (*MockdocStore) List

func (_m *MockdocStore) List(ctx context.Context, repo string) ([]DocumentMeta, error)

List provides a mock function with given fields: ctx, repo

func (*MockdocStore) ListAssets

func (_m *MockdocStore) ListAssets(ctx context.Context, repo string) ([]string, error)

ListAssets provides a mock function with given fields: ctx, repo

func (*MockdocStore) ListRepos

func (_m *MockdocStore) ListRepos(ctx context.Context) ([]RepoInfo, error)

ListRepos provides a mock function with given fields: ctx

func (*MockdocStore) Save

func (_m *MockdocStore) Save(ctx context.Context, doc Document) error

Save provides a mock function with given fields: ctx, doc

func (*MockdocStore) SaveAsset

func (_m *MockdocStore) SaveAsset(ctx context.Context, repo string, path string, data []byte) error

SaveAsset provides a mock function with given fields: ctx, repo, path, data

type MockdocStore_DeleteAsset_Call

type MockdocStore_DeleteAsset_Call struct {
	*mock.Call
}

MockdocStore_DeleteAsset_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAsset'

func (*MockdocStore_DeleteAsset_Call) Return

func (*MockdocStore_DeleteAsset_Call) Run

func (*MockdocStore_DeleteAsset_Call) RunAndReturn

type MockdocStore_Delete_Call

type MockdocStore_Delete_Call struct {
	*mock.Call
}

MockdocStore_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'

func (*MockdocStore_Delete_Call) Return

func (*MockdocStore_Delete_Call) Run

func (_c *MockdocStore_Delete_Call) Run(run func(ctx context.Context, repo string, path string)) *MockdocStore_Delete_Call

func (*MockdocStore_Delete_Call) RunAndReturn

type MockdocStore_Expecter

type MockdocStore_Expecter struct {
	// contains filtered or unexported fields
}

func (*MockdocStore_Expecter) Delete

func (_e *MockdocStore_Expecter) Delete(ctx interface{}, repo interface{}, path interface{}) *MockdocStore_Delete_Call

Delete is a helper method to define mock.On call

  • ctx context.Context
  • repo string
  • path string

func (*MockdocStore_Expecter) DeleteAsset

func (_e *MockdocStore_Expecter) DeleteAsset(ctx interface{}, repo interface{}, path interface{}) *MockdocStore_DeleteAsset_Call

DeleteAsset is a helper method to define mock.On call

  • ctx context.Context
  • repo string
  • path string

func (*MockdocStore_Expecter) Get

func (_e *MockdocStore_Expecter) Get(ctx interface{}, repo interface{}, path interface{}) *MockdocStore_Get_Call

Get is a helper method to define mock.On call

  • ctx context.Context
  • repo string
  • path string

func (*MockdocStore_Expecter) GetAsset

func (_e *MockdocStore_Expecter) GetAsset(ctx interface{}, repo interface{}, path interface{}) *MockdocStore_GetAsset_Call

GetAsset is a helper method to define mock.On call

  • ctx context.Context
  • repo string
  • path string

func (*MockdocStore_Expecter) List

func (_e *MockdocStore_Expecter) List(ctx interface{}, repo interface{}) *MockdocStore_List_Call

List is a helper method to define mock.On call

  • ctx context.Context
  • repo string

func (*MockdocStore_Expecter) ListAssets

func (_e *MockdocStore_Expecter) ListAssets(ctx interface{}, repo interface{}) *MockdocStore_ListAssets_Call

ListAssets is a helper method to define mock.On call

  • ctx context.Context
  • repo string

func (*MockdocStore_Expecter) ListRepos

func (_e *MockdocStore_Expecter) ListRepos(ctx interface{}) *MockdocStore_ListRepos_Call

ListRepos is a helper method to define mock.On call

  • ctx context.Context

func (*MockdocStore_Expecter) Save

func (_e *MockdocStore_Expecter) Save(ctx interface{}, doc interface{}) *MockdocStore_Save_Call

Save is a helper method to define mock.On call

  • ctx context.Context
  • doc Document

func (*MockdocStore_Expecter) SaveAsset

func (_e *MockdocStore_Expecter) SaveAsset(ctx interface{}, repo interface{}, path interface{}, data interface{}) *MockdocStore_SaveAsset_Call

SaveAsset is a helper method to define mock.On call

  • ctx context.Context
  • repo string
  • path string
  • data []byte

type MockdocStore_GetAsset_Call

type MockdocStore_GetAsset_Call struct {
	*mock.Call
}

MockdocStore_GetAsset_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAsset'

func (*MockdocStore_GetAsset_Call) Return

func (*MockdocStore_GetAsset_Call) Run

func (*MockdocStore_GetAsset_Call) RunAndReturn

type MockdocStore_Get_Call

type MockdocStore_Get_Call struct {
	*mock.Call
}

MockdocStore_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'

func (*MockdocStore_Get_Call) Return

func (*MockdocStore_Get_Call) Run

func (_c *MockdocStore_Get_Call) Run(run func(ctx context.Context, repo string, path string)) *MockdocStore_Get_Call

func (*MockdocStore_Get_Call) RunAndReturn

type MockdocStore_ListAssets_Call

type MockdocStore_ListAssets_Call struct {
	*mock.Call
}

MockdocStore_ListAssets_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListAssets'

func (*MockdocStore_ListAssets_Call) Return

func (*MockdocStore_ListAssets_Call) Run

func (*MockdocStore_ListAssets_Call) RunAndReturn

type MockdocStore_ListRepos_Call

type MockdocStore_ListRepos_Call struct {
	*mock.Call
}

MockdocStore_ListRepos_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepos'

func (*MockdocStore_ListRepos_Call) Return

func (*MockdocStore_ListRepos_Call) Run

func (*MockdocStore_ListRepos_Call) RunAndReturn

type MockdocStore_List_Call

type MockdocStore_List_Call struct {
	*mock.Call
}

MockdocStore_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List'

func (*MockdocStore_List_Call) Return

func (*MockdocStore_List_Call) Run

func (_c *MockdocStore_List_Call) Run(run func(ctx context.Context, repo string)) *MockdocStore_List_Call

func (*MockdocStore_List_Call) RunAndReturn

type MockdocStore_SaveAsset_Call

type MockdocStore_SaveAsset_Call struct {
	*mock.Call
}

MockdocStore_SaveAsset_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveAsset'

func (*MockdocStore_SaveAsset_Call) Return

func (*MockdocStore_SaveAsset_Call) Run

func (_c *MockdocStore_SaveAsset_Call) Run(run func(ctx context.Context, repo string, path string, data []byte)) *MockdocStore_SaveAsset_Call

func (*MockdocStore_SaveAsset_Call) RunAndReturn

type MockdocStore_Save_Call

type MockdocStore_Save_Call struct {
	*mock.Call
}

MockdocStore_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save'

func (*MockdocStore_Save_Call) Return

func (*MockdocStore_Save_Call) Run

func (*MockdocStore_Save_Call) RunAndReturn

type MocksearchEngine

type MocksearchEngine struct {
	mock.Mock
}

MocksearchEngine is an autogenerated mock type for the searchEngine type

func NewMocksearchEngine

func NewMocksearchEngine(t interface {
	mock.TestingT
	Cleanup(func())
}) *MocksearchEngine

NewMocksearchEngine creates a new instance of MocksearchEngine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. The first argument is typically a *testing.T value.

func (*MocksearchEngine) EXPECT

func (*MocksearchEngine) Index

func (_m *MocksearchEngine) Index(ctx context.Context, doc Document, plainText string) error

Index provides a mock function with given fields: ctx, doc, plainText

func (*MocksearchEngine) ListByRepo

func (_m *MocksearchEngine) ListByRepo(ctx context.Context, repo string) ([]string, error)

ListByRepo provides a mock function with given fields: ctx, repo

func (*MocksearchEngine) Remove

func (_m *MocksearchEngine) Remove(ctx context.Context, docID string) error

Remove provides a mock function with given fields: ctx, docID

func (*MocksearchEngine) Search

func (_m *MocksearchEngine) Search(ctx context.Context, query string, opts SearchOpts) (*SearchResults, error)

Search provides a mock function with given fields: ctx, query, opts

type MocksearchEngine_Expecter

type MocksearchEngine_Expecter struct {
	// contains filtered or unexported fields
}

func (*MocksearchEngine_Expecter) Index

func (_e *MocksearchEngine_Expecter) Index(ctx interface{}, doc interface{}, plainText interface{}) *MocksearchEngine_Index_Call

Index is a helper method to define mock.On call

  • ctx context.Context
  • doc Document
  • plainText string

func (*MocksearchEngine_Expecter) ListByRepo

func (_e *MocksearchEngine_Expecter) ListByRepo(ctx interface{}, repo interface{}) *MocksearchEngine_ListByRepo_Call

ListByRepo is a helper method to define mock.On call

  • ctx context.Context
  • repo string

func (*MocksearchEngine_Expecter) Remove

func (_e *MocksearchEngine_Expecter) Remove(ctx interface{}, docID interface{}) *MocksearchEngine_Remove_Call

Remove is a helper method to define mock.On call

  • ctx context.Context
  • docID string

func (*MocksearchEngine_Expecter) Search

func (_e *MocksearchEngine_Expecter) Search(ctx interface{}, query interface{}, opts interface{}) *MocksearchEngine_Search_Call

Search is a helper method to define mock.On call

  • ctx context.Context
  • query string
  • opts SearchOpts

type MocksearchEngine_Index_Call

type MocksearchEngine_Index_Call struct {
	*mock.Call
}

MocksearchEngine_Index_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Index'

func (*MocksearchEngine_Index_Call) Return

func (*MocksearchEngine_Index_Call) Run

func (*MocksearchEngine_Index_Call) RunAndReturn

type MocksearchEngine_ListByRepo_Call

type MocksearchEngine_ListByRepo_Call struct {
	*mock.Call
}

MocksearchEngine_ListByRepo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListByRepo'

func (*MocksearchEngine_ListByRepo_Call) Return

func (*MocksearchEngine_ListByRepo_Call) Run

func (*MocksearchEngine_ListByRepo_Call) RunAndReturn

type MocksearchEngine_Remove_Call

type MocksearchEngine_Remove_Call struct {
	*mock.Call
}

MocksearchEngine_Remove_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Remove'

func (*MocksearchEngine_Remove_Call) Return

func (*MocksearchEngine_Remove_Call) Run

func (*MocksearchEngine_Remove_Call) RunAndReturn

type MocksearchEngine_Search_Call

type MocksearchEngine_Search_Call struct {
	*mock.Call
}

MocksearchEngine_Search_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Search'

func (*MocksearchEngine_Search_Call) Return

func (*MocksearchEngine_Search_Call) Run

func (*MocksearchEngine_Search_Call) RunAndReturn

type RepoInfo

type RepoInfo struct {
	LastUpdated time.Time `json:"last_updated"`
	Name        string    `json:"name"`
	DocCount    int       `json:"doc_count"`
}

RepoInfo contains metadata about an indexed repository.

type SearchOpts

type SearchOpts struct {
	Limit  int
	Offset int
}

SearchOpts configures search behavior.

type SearchResult

type SearchResult struct {
	ID               string
	Repo             string
	Path             string
	Title            string
	Anchor           string   // heading anchor ID to deep-link into the document (may be empty)
	TitleFragments   []string // highlighted fragments from the title field
	ContentFragments []string // highlighted fragments from the content field
	Score            float64
}

SearchResult represents a single search result with highlighted snippets.

type SearchResults

type SearchResults struct {
	Hits     []SearchResult
	Total    uint64
	Duration time.Duration
}

SearchResults holds the response from a search query.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service encapsulates core business logic and dependencies.

func New

func New(store docStore, search searchEngine, processors map[ContentType]ContentProcessor) *Service

New creates a new Service instance with the provided dependencies. The processors map must contain at least a ContentTypeMarkdown entry. It panics if processors is nil or does not contain a markdown processor, since markdown is the default fallback for unknown content types.

func (*Service) GetAsset

func (s *Service) GetAsset(ctx context.Context, repo, path string) ([]byte, error)

GetAsset retrieves a binary asset by its repository and path.

func (*Service) GetDocument

func (s *Service) GetDocument(ctx context.Context, repo, path string) (Document, []byte, []Heading, error)

GetDocument retrieves a document and renders its content to HTML using the appropriate content processor. It also extracts headings for table of contents navigation. Relative image URLs in the rendered HTML are rewritten to point to the asset serving route.

func (*Service) IngestDocuments

func (s *Service) IngestDocuments(ctx context.Context, req *IngestRequest) (*IngestResponse, error)

IngestDocuments processes a batch of document upserts and deletes from a repository. When req.Sync is true, after processing all documents the server treats the incoming document set as the complete truth for the repo and removes any stored documents whose paths are not present in the request. Assets (images, etc.) bundled in the request are stored alongside documents and participate in sync cleanup.

func (*Service) ListDocuments

func (s *Service) ListDocuments(ctx context.Context, repo string) ([]DocumentMeta, error)

ListDocuments returns metadata for all documents in a repository.

func (*Service) ListRepos

func (s *Service) ListRepos(ctx context.Context) ([]RepoInfo, error)

ListRepos returns metadata for all indexed repositories.

func (*Service) SearchDocs

func (s *Service) SearchDocs(ctx context.Context, query string, opts SearchOpts) (*SearchResults, error)

SearchDocs performs a full-text search across all indexed documents. After retrieving results from the search engine it attempts to resolve a heading anchor for each hit so that the result link can scroll directly to the matching section. Anchor resolution is best-effort; failures are logged and do not prevent results from being returned.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL