Documentation
¶
Overview ¶
Package config owns everything PayCLI knows before it talks to a server: where its files live (§4.1), what is in them (§4.2, §4.3), how the layers combine (§4.5) and what can be learned about the surrounding Payload project from the local filesystem alone (§7.10, §7.11).
Two rules shape this package:
- Resolve is a pure function. It performs no I/O whatsoever, so §4.5's precedence matrix is a table test (§3.1).
- Nothing here reads the process environment directly. It enters as an Env snapshot built once by internal/cli/app.go, which is the only file allowed to touch it (§3.1).
Index ¶
- Constants
- Variables
- func BlockSlugsFromSource(src string) []string
- func Interpolate(s string, env Env) (string, []string)
- func InterpolateMap(in map[string]string, env Env) (map[string]string, []string)
- func ProfileNames(files ...*File) []string
- type AuthMode
- type BlockDecl
- type BlockFieldDecl
- type CacheSection
- type Defaults
- type Env
- type FieldDoc
- type File
- type Flags
- type Input
- type KeyringMode
- type LoggingSection
- type Paths
- func (p Paths) AuditFile() string
- func (p Paths) CacheRoot() string
- func (p Paths) CredentialsFile() string
- func (p Paths) Map() map[string]string
- func (p Paths) ScopeDir(scope string) string
- func (p Paths) UpdateStateFile() string
- func (p Paths) WithCacheDir(dir string) Paths
- func (p Paths) WithConfigFile(path string) Paths
- type Profile
- type Project
- type Resolved
- type ScanResult
- type Setting
- type UpdateSection
Constants ¶
const ( KindUser = "user" KindProject = "project" )
Layer kinds for the source strings of §4.5.
const ( ConfigFileName = "config.toml" CredentialsFileName = "credentials.json" AuditFileName = "audit.log" UpdateStateFileName = "update-state.json" ProjectFileName = "pay.toml" // CacheGeneration is the versioned sub-directory of the cache dir; the // whole tree below it is disposable (§4.1, §8). CacheGeneration = "v1" )
File names inside the resolved directories (§4.1).
const ( DBPostgres = "postgres" DBMongoDB = "mongodb" DBSQLite = "sqlite" DBUnknown = "unknown" )
DBAdapter values (§7.11).
const ( SourceConfigured = "configured" SourceProjectPkg = "project-package-json" SourceProjectSource = "project-source" SourceInferred = "inferred" SourceObserved = "observed" SourceUnknown = "unknown" )
Provenance strings shared by §7.10/§7.11 and `pay config explain`.
const ( MaxScanFiles = 200 MaxScanBytes = 2 << 20 // 2 MB total )
Scan caps (§7.10). They exist so that pointing PayCLI at a monorepo cannot turn a cache miss into a filesystem crawl.
const ( DefaultProfileName = "default" DefaultAPIPath = "/api" DefaultGraphQLRoute = "/graphql" DefaultAuthCollection = "auto" DefaultAuthHeaderScheme = "JWT" DefaultOutput = "json" DefaultErrorsTo = "stdout" DefaultDepth = 0 DefaultLimit = 20 DefaultMaxRetries = 3 DefaultConcurrency = 8 DefaultMaxBulk = 100 DefaultAcceptLanguage = "en" DefaultLogLevel = "info" DefaultLogFormat = "text" DefaultUpdateChannel = "stable" // MaxConcurrency is §9.1's hard cap. PayCLI is the only backpressure a // Payload 3.x server has. MaxConcurrency = 32 )
Built-in defaults (§4.2). These are the last link of §4.5's chain.
const ( DefaultTimeout = 30 * time.Second DefaultDeadline = 120 * time.Second DefaultDiscoveryTTL = 24 * time.Hour DefaultAccessTTL = 10 * time.Minute DefaultSchemaTTL = 24 * time.Hour DefaultIdentityTTL = 60 * time.Second DefaultSkillsTTL = 6 * time.Hour )
Default durations (§4.2).
const ( SourceFlag = "flag" SourceDefault = "default" // SourceDerivedGraphQL is §4.2's visible coupling between api_path and // graphql_route. SourceDerivedGraphQL = "derived:api_path+graphql_route" )
Source strings that are not a layer name.
const FilePerm fs.FileMode = 0o600
FilePerm is the mode of config.toml (§4.1). It is 0600 like everything else PayCLI writes: the file holds header values that §5.3 treats as secret.
Variables ¶
var AuthModes = []AuthMode{AuthModeAuto, AuthModeAPIKey, AuthModeJWT, AuthModeAnonymous}
AuthModes is the accepted set, in help order.
var DescriptionKeys = []string{
"admin.description", "custom.description", "custom.docs", "custom.summary",
}
DescriptionKeys are the config paths §7.10 accepts a human description from, in preference order, and the exact strings reported back as description_key.
`admin.description` is Payload's own documented FIELD-level key and is both the right place for per-field docs and the first choice here. A BLOCK has no such key: a Block's `admin` accepts only components / custom / disableBlockName / group / images / jsx, and `tsc --noEmit` rejects `admin: { description }` on one. So a block's description has to live in `custom`, Payload's sanctioned arbitrary-metadata escape hatch — which is free-form, meaning every project spells it differently. The obvious neighbours are therefore all accepted, and whichever one answered is always reported next to the text rather than being flattened into a "description" that implies Payload defines one.
Functions ¶
func BlockSlugsFromSource ¶
BlockSlugsFromSource extracts the `slug: '…'` half of BlockDeclsFromSource, in the same order and with the same duplicate handling.
func Interpolate ¶
Interpolate expands ${NAME} references in a config value against env (§4.2's `"X-Vercel-Protection-Bypass" = "${VERCEL_BYPASS}"`).
Deliberately minimal, because this string can end up in an HTTP header and in §8.1's headersFP:
- ${NAME} only. A bare $NAME is left alone, so a literal dollar in a token survives untouched.
- $${NAME} escapes to the literal ${NAME}.
- An unset (or set-but-empty, §4.5) variable expands to "" and its name is returned in missing, so the caller can warn instead of silently sending an empty header.
The returned missing slice is sorted and de-duplicated.
func InterpolateMap ¶
InterpolateMap expands every value of a header map. Keys are never interpolated: a header *name* built from the environment would make §8.1's headersFP and the redaction allow-list unpredictable.
func ProfileNames ¶
ProfileNames returns the union of profile names across the layers, sorted, for did-you-mean and `pay auth list`.
Types ¶
type AuthMode ¶
type AuthMode string
AuthMode is §5.0's three-way auth selector plus "auto".
const ( AuthModeAuto AuthMode = "auto" AuthModeAPIKey AuthMode = "api-key" AuthModeJWT AuthMode = "jwt" AuthModeAnonymous AuthMode = "anonymous" )
Auth modes. "auto" is a *configuration* value only: it never survives credential resolution, which always lands on one of the other three (§5.1).
func ParseAuthMode ¶
ParseAuthMode validates --auth-mode / PAY_AUTH_MODE / the profile key.
type BlockDecl ¶ added in v0.2.0
type BlockDecl struct {
Slug string `json:"slug"`
// InterfaceName is "" for a block that declares none; Payload then derives
// the GraphQL type name from the slug itself.
InterfaceName string `json:"interface_name,omitempty"`
// Fields are the entries of this block's own `fields:` array literal, in
// source order. They exist for ONE fact GraphQL cannot supply: Payload
// generates no INPUT_OBJECT for a block type (the blocks mutation argument
// is a JSON scalar), so the NON_NULL trick that recovers required-ness for
// a collection field has nothing to read. The config on disk is the only
// place `required: true` is written down.
Fields []BlockFieldDecl `json:"fields,omitempty"`
// FieldsComplete is true only when EVERY element of the `fields:` array
// was an object literal carrying a `name:` string literal. A helper call
// (`linkGroup({…})`), a spread, or a computed field makes it false, and a
// false here is what keeps a missing entry reading as "unknown" instead of
// "not required" — the exact guess this whole type exists to refuse.
FieldsComplete bool `json:"fields_complete,omitempty"`
// LabelSingular and LabelPlural are the block's own `labels:` literals
// ({ singular: 'Call to Action', plural: 'Calls to Action' }). They are
// the human name of the block and, like every other fact here, exist only
// when the project's authors wrote them; "" is "not declared", never a
// title-cased guess at the slug.
LabelSingular string `json:"label_singular,omitempty"`
LabelPlural string `json:"label_plural,omitempty"`
// Description is the one-sentence statement of what this block is FOR.
//
// Payload blocks have NO standard field for it — verified: a Block's
// `admin` accepts only components/custom/disableBlockName/group/images/jsx
// and `tsc --noEmit` rejects `admin: { description }` on a Block — so the
// text lives in `custom`, Payload's sanctioned arbitrary-metadata escape
// hatch, under whatever key the project chose. DescriptionKey records
// which one it was so the output is honest about where the words came from
// instead of implying a standard field exists.
Description string `json:"description,omitempty"`
// DescriptionKey is the literal config path the text was read from:
// "custom.description", "custom.docs", "custom.summary" or
// "admin.description". Empty exactly when Description is empty.
DescriptionKey string `json:"description_key,omitempty"`
}
BlockDecl is one block definition found in project source: the blockType slug the REST API accepts, together with the interfaceName Payload turns into that block's GraphQL union member.
Verified in /home/flo/payload-dummy: src/blocks/CallToAction/config.ts declares slug: 'cta' beside interfaceName: 'CallToActionBlock', and __type(name:"Page_Layout").possibleTypes names CallToActionBlock. Without the pair there is no way back from the union to the slug.
func BlockDeclsFromSource ¶ added in v0.2.0
BlockDeclsFromSource extracts the `slug: '…'` and `interfaceName: '…'` literals of every object literal that also has a `fields:` key (§7.10).
The two are harvested TOGETHER, from the same object literal, because the pair is the only bridge between what GraphQL publishes for a blocks field (possibleTypes: CallToActionBlock) and what the REST API accepts for it (blockType: cta). A bare slug list cannot say which field a block belongs to, which is exactly how one global bag ended up attached to every blocks field.
This is a byte scanner, not a TypeScript parser: PayCLI must never import or evaluate project code. It understands strings, template literals and both comment forms well enough that a slug inside one of them cannot be mistaken for a declaration, and it pairs slug/fields by brace nesting, so a nested block definition is picked up while an unrelated `slug:` on a collection export without `fields:` is not.
Order is source order; duplicate slugs are removed.
type BlockFieldDecl ¶ added in v0.2.0
type BlockFieldDecl struct {
Name string `json:"name"`
// Type is the `type:` literal ('richText', 'upload', 'select', …). It is
// the only source that can tell lexical richText from a json field and a
// select from a radio, both of which compile to the SAME GraphQL shape.
Type string `json:"type,omitempty"`
// Required is the literal `required: true` / `required: false`, or nil
// when the key was absent or not a boolean literal.
Required *bool `json:"required,omitempty"`
// Description is the field's own human instruction, read from
// `admin: { description: '…' }` — which, unlike a Block's admin, Payload
// DOES support on a field and is exactly where a per-field "pass a media
// document id" belongs. `custom.description` is accepted as well, for the
// same reason it is on a block.
Description string `json:"description,omitempty"`
// DescriptionKey is the literal config path the text was read from
// ("admin.description", "custom.description", …), empty when Description
// is empty.
DescriptionKey string `json:"description_key,omitempty"`
}
BlockFieldDecl is one field read out of a block's `fields:` array.
Required is a POINTER: nil is "the object literal had no `required:` key at all", which for Payload means not required, while a non-literal value (`required: isProd`) is also recorded as nil because the scanner refuses to evaluate project code. The caller decides what to do with each, and only a declaration that was fully parsed is allowed to answer false.
type CacheSection ¶
type CacheSection struct {
Dir string `toml:"dir,omitempty"`
DiscoveryTTL string `toml:"discovery_ttl,omitempty"`
AccessTTL string `toml:"access_ttl,omitempty"`
SchemaTTL string `toml:"schema_ttl,omitempty"`
IdentityTTL string `toml:"identity_ttl,omitempty"`
SkillsTTL string `toml:"skills_ttl,omitempty"`
}
CacheSection is the [cache] table (§4.2).
type Defaults ¶
type Defaults struct {
Output string `toml:"output,omitempty"`
Depth *int `toml:"depth,omitempty"`
Limit *int `toml:"limit,omitempty"`
Timeout string `toml:"timeout,omitempty"`
Deadline string `toml:"deadline,omitempty"`
MaxRetries *int `toml:"max_retries,omitempty"`
Concurrency *int `toml:"concurrency,omitempty"`
Redact *bool `toml:"redact,omitempty"`
AcceptLanguage string `toml:"accept_language,omitempty"`
MaxBulk *int `toml:"max_bulk,omitempty"`
ConfirmWrites *bool `toml:"confirm_writes,omitempty"`
Locale string `toml:"locale,omitempty"`
FallbackLocale string `toml:"fallback_locale,omitempty"`
ErrorsTo string `toml:"errors_to,omitempty"`
}
Defaults is the [defaults] table (§4.2). Pointers everywhere: `depth = 0` and `redact = false` are meaningful settings, not absence.
type Env ¶
Env is a snapshot of the process environment.
Lookup implements §4.5's "set but empty counts as unset" rule, so `PAY_API_KEY= pay …` cannot silently blank a credential.
type FieldDoc ¶ added in v0.2.0
type FieldDoc struct {
Description string `json:"description"`
// Key is the literal config path — "admin.description" in the normal case.
Key string `json:"key"`
}
FieldDoc is one field's human documentation as the project wrote it: the text and the config key it came from, never one without the other.
type File ¶
type File struct {
Version int `toml:"version,omitempty"`
DefaultProfile string `toml:"default_profile,omitempty"`
Defaults Defaults `toml:"defaults,omitempty"`
Cache CacheSection `toml:"cache,omitempty"`
Logging LoggingSection `toml:"logging,omitempty"`
Update UpdateSection `toml:"update,omitempty"`
Profiles map[string]Profile `toml:"profiles,omitempty"`
// APIKey is not a legal key anywhere (§4.2); it is decoded only so the
// hard error can name it.
//nolint:gosec // G117: decode-only. Encode() clears it; see
// TestEncodeNeverEmitsAPIKey and TestAPIKeyInConfigIsFatal.
APIKey string `toml:"api_key,omitempty"`
// Path is where the file came from; it is what `pay config explain`
// prints as "user:/abs/config.toml" or "project:/abs/pay.toml" (§4.5).
Path string `toml:"-"`
// Exists is false for a file that was absent, which is not an error.
Exists bool `toml:"-"`
// Kind is "user" or "project"; it prefixes the source string.
Kind string `toml:"-"`
// UnknownKeys are decoded-but-unrecognised keys, surfaced as warnings so a
// typo like `base_ur1` is visible without being fatal.
UnknownKeys []string `toml:"-"`
}
File is a decoded config.toml (§4.2) or pay.toml (§4.3). The same schema serves both; a project file simply must not carry secrets.
func Load ¶
Load reads and validates a config file. A missing file yields a usable empty File with Exists == false and no error: PayCLI must work with no config at all, driven entirely by flags and environment (§4.5, §21).
func Parse ¶
Parse decodes TOML bytes. It is the I/O-free half of Load, which is what the tests use.
func (*File) Encode ¶
Encode renders the file as TOML.
APIKey is cleared first. The field exists only so a config.toml carrying the forbidden `api_key` decodes and can be rejected by name (§4.2); it must never travel in the other direction. Without this, any code path that set it would make Encode write a credential into config.toml — exactly what §5.2 forbids and what scripts/arch-lint.sh greps for. Encode operates on a copy, so the caller's File is untouched.
func (*File) Save ¶
Save writes the file atomically at 0600 (§4.1). Version is forced to 1 so a hand-written file that omitted it does not round-trip as 0.
func (*File) SetProfile ¶
SetProfile inserts or replaces a profile.
func (*File) Source ¶
Source renders this layer's §4.5 source string, e.g. "user:/home/u/.config/pay/config.toml".
type Flags ¶
type Flags struct {
Profile string
BaseURL string
APIPath string
GraphQLPath string
GraphQLRoute string
AuthCollection string
AuthMode string
AuthHeaderScheme string
Output string
ErrorsTo string
Path string
Locale string
FallbackLocale string
AcceptLanguage string
Timeout string
Deadline string
CacheTTL string
Depth *int
Limit *int
MaxDocs *int
MaxRetries *int
Concurrency *int
Redact *bool
InsecureSkipVerify *bool
NoCache *bool
Refresh *bool
Yes *bool
DryRun *bool
NoAudit *bool
Quiet *bool
Verbose *bool
LogLevel string
LogFormat string
}
Flags carries §9.1's root persistent flags. Every field is empty/nil unless the flag was actually changed, which is how "flag" wins §4.5's first slot without a spurious zero value beating a config file.
type Input ¶
Input is everything Resolve is allowed to see. No file handles, no clock, no environment access: the caller loads the layers, Resolve combines them (§3.1).
type KeyringMode ¶
type KeyringMode string
KeyringMode is PAY_KEYRING (§5.2).
const ( KeyringAuto KeyringMode = "auto" KeyringOff KeyringMode = "off" KeyringForce KeyringMode = "force" )
Keyring modes.
func ParseKeyringMode ¶
func ParseKeyringMode(s string) (KeyringMode, error)
ParseKeyringMode validates PAY_KEYRING. The default is "off": §5.1 step 8 reads the keychain only when the profile opted in or PAY_KEYRING=force, so an unset variable must not make the chain touch dbus/wincred. "auto" only affects *writes* (§5.2).
type LoggingSection ¶
type LoggingSection struct {
Level string `toml:"level,omitempty"`
Format string `toml:"format,omitempty"`
}
LoggingSection is the [logging] table (§4.2).
type Paths ¶
type Paths struct {
ConfigDir string
CacheDir string
StateDir string
ConfigFile string
Sources map[string]string
// contains filtered or unexported fields
}
Paths is the one source of truth for where PayCLI keeps its files (§4.1). Sources explains, per directory, which rule produced it, so `pay config paths --output json` never has to guess.
func DefaultPaths ¶
DefaultPaths is PathsFor bound to this host. It is the only function in the package that touches the OS, and it never fails: an unknown home directory degrades to the process working directory rather than aborting the CLI.
func PathsFor ¶
PathsFor is the pure form of path resolution: everything it needs is an argument, so the Windows and XDG branches are both testable on any host.
goos is a runtime.GOOS value; home is the user's home directory (may be empty, in which case relative fallbacks are used rather than failing — a missing $HOME must never make `pay version` impossible).
Precedence per directory: the specific PAY_*_DIR variable, then $PAY_HOME, then the platform rule. PAY_HOME "overrides all three" defaults (§4.1) but the more specific variable still wins over it — that is the conventional reading, and it lets a test pin PAY_HOME while redirecting just the cache.
func (Paths) CacheRoot ¶
CacheRoot is the generation directory; `rm -rf` on it must only ever cost time (§4.1).
func (Paths) CredentialsFile ¶
CredentialsFile is §4.4's 0600 credential store.
func (Paths) UpdateStateFile ¶
UpdateStateFile is §15's self-update bookkeeping.
func (Paths) WithCacheDir ¶
WithCacheDir applies config.toml's [cache] dir override (§4.2).
func (Paths) WithConfigFile ¶
WithConfigFile applies the --config flag, which points at the config file itself and therefore also moves nothing else (§9.1).
type Profile ¶
type Profile struct {
BaseURL string `toml:"base_url,omitempty"`
APIPath string `toml:"api_path,omitempty"`
GraphQLRoute string `toml:"graphql_route,omitempty"`
GraphQLPath string `toml:"graphql_path,omitempty"`
AuthCollection string `toml:"auth_collection,omitempty"`
AuthMode string `toml:"auth_mode,omitempty"`
AuthHeaderScheme string `toml:"auth_header_scheme,omitempty"`
Label string `toml:"label,omitempty"`
APIKeyEnv string `toml:"api_key_env,omitempty"`
CredentialHelper string `toml:"credential_helper,omitempty"`
InsecureSkipVerify *bool `toml:"insecure_skip_verify,omitempty"`
Headers map[string]string `toml:"headers,omitempty"`
Blocks map[string][]string `toml:"blocks,omitempty"`
// Optional pins. Each one turns a discovered-or-unknown value into a
// configured one and flips the matching *_source to "configured" (§4.2).
IDType string `toml:"id_type,omitempty"`
Locales []string `toml:"locales,omitempty"`
PayloadVersion string `toml:"payload_version,omitempty"`
DBAdapter string `toml:"db_adapter,omitempty"`
EchoCheckIgnore []string `toml:"echo_check_ignore,omitempty"`
CustomEndpoints []string `toml:"custom_endpoints,omitempty"`
// APIKey is never a legal key (§4.2); see File.Validate.
APIKey string `toml:"api_key,omitempty"`
}
Profile is one [profiles.X] table (§4.2).
Every optional scalar is a pointer or an empty-able string so that "unset" and "set to the zero value" stay distinguishable through §4.5's chain. The APIKey field exists only so that a config.toml carrying the forbidden key decodes instead of landing in the undecoded set: its presence is a hard error (config_secret_in_plaintext).
type Project ¶
type Project struct {
// Dir is the directory that anchored the discovery — the first one going
// up that contained any marker at all.
Dir string
// ConfigPath is ./pay.toml, the committed project config (§4.3).
ConfigPath string
// PackageJSON is the nearest package.json (§7.11 source 2).
PackageJSON string
// PayloadConfig is the nearest payload.config.ts / .js / .mjs, at the root
// or under src/ (§7.10).
PayloadConfig string
// SrcDir is the payload config's directory when it lives in src/.
SrcDir string
// GitRoot is the directory holding .git; the walk stops after it.
GitRoot string
// Start is the directory the walk began in.
Start string
// contains filtered or unexported fields
}
Project is what the upward walk of §4.3 found around the working directory. Every field is optional: PayCLI is usable from any directory, including one that has nothing to do with a Payload project.
func FindProject ¶
FindProject walks up from start looking for a project (§4.3). It stops at the git root, at the filesystem root, or after maxWalkDepth directories.
The walk never leaves the filesystem and never executes anything it finds.
func (*Project) GitignoreCovers ¶
GitignoreCovers reports whether the project's .gitignore mentions pattern. `pay auth login` uses it to warn when a skill directory or a credential file would be committed (§5.3). A missing .gitignore reports false.
func (*Project) IsPayload ¶
IsPayload reports whether this looks like a Payload project specifically — the precondition for §7.10's block-slug scan and §7.11's version read.
func (*Project) LoadConfig ¶
LoadConfig loads the project pay.toml, if the walk found one. A project without pay.toml is normal and yields an empty, non-existent File.
type Resolved ¶
type Resolved struct {
Profile string
ProfileDefined bool
Label string
BaseURL string
APIPath string
GraphQLRoute string
GraphQLPath string
AuthCollection string
AuthMode AuthMode
AuthHeaderScheme string
APIKeyEnv string
CredentialHelper string
Headers map[string]string
KeyringMode KeyringMode
Output string
ErrorsTo string
Path string
Locale string
FallbackLocale string
AcceptLanguage string
Timeout time.Duration
Deadline time.Duration
Depth int
Limit int
MaxDocs int
MaxRetries int
Concurrency int
MaxBulk int
Redact bool
ConfirmWrites bool
InsecureSkipVerify bool
NoCache bool
Refresh bool
CacheTTL time.Duration
Yes bool
DryRun bool
NoAudit bool
Quiet bool
Verbose bool
CacheDir string
DiscoveryTTL time.Duration
AccessTTL time.Duration
SchemaTTL time.Duration
IdentityTTL time.Duration
SkillsTTL time.Duration
LogLevel string
LogFormat string
UpdateAuto bool
UpdateChannel string
// Optional pins (§4.2). An empty value means "not configured"; the
// matching *Source entry says so explicitly.
IDType string
Locales []string
PayloadVersion string
DBAdapter string
EchoCheckIgnore []string
CustomEndpoints []string
Blocks map[string][]string
// ConcurrencyClamped is true when the requested concurrency exceeded
// MaxConcurrency and was lowered.
ConcurrencyClamped bool
// Warnings are non-fatal configuration observations for the caller to put
// in the envelope (§10): a moved base_url credential, an unset ${ENV} in a
// header, an unknown key in a config file.
Warnings []string
// Sources maps a setting name to its §4.5 provenance string.
Sources map[string]string
// contains filtered or unexported fields
}
Resolved is the effective configuration for one command invocation.
basicAuth is deliberately unexported and has no JSON/TOML tag anywhere: it holds a credential lifted out of base_url (§4.2) and must never be serialised with the rest of the struct.
func Resolve ¶
Resolve combines the layers of §4.5. It is pure: given the same Input it returns the same Resolved, and it never touches the filesystem, the clock or the environment beyond the supplied snapshot.
func (*Resolved) BasicAuth ¶
BasicAuth returns the credential that was carried in base_url, if any (§4.2). The transport applies it as an Authorization: Basic header, where it is redacted like every other credential.
func (*Resolved) Explain ¶
Explain renders `pay config explain --output json` (§4.5). Header values are masked: the names are the only part that may be shown (§5.3).
func (*Resolved) HeaderNames ¶
HeaderNames returns the configured extra header names, sorted. Only names ever leave this process; values are secret (§5.3, §8.1).
func (*Resolved) NormURL ¶
NormURL is §8.1's normalised URL: lowercase scheme and host, the port only when it is non-default, plus api_path. Userinfo is already gone by construction. It lives here because it is a pure function of resolved config, and internal/cache must not re-derive it differently.
func (*Resolved) RequireBaseURL ¶
RequireBaseURL is the check every command that talks to a server performs. Resolve itself tolerates an absent base_url so that `pay version`, `pay config paths` and `pay explain` still work on a bare machine.
type ScanResult ¶
type ScanResult struct {
Root string `json:"root,omitempty"`
PayloadVersion string `json:"payload_version,omitempty"`
PayloadVersionSource string `json:"payload_version_source"`
PackageJSON string `json:"package_json,omitempty"`
// DBAdapter is inferred from the project's @payloadcms/db-* dependency.
// §7.11 only defines "configured" and "inferred" for this field, so a
// package.json match reports "inferred" — which deliberately does NOT
// unlock §9.3's client-side operator block, since that requires
// "configured".
DBAdapter string `json:"db_adapter"`
DBAdapterSource string `json:"db_adapter_source"`
BlockSlugs []string `json:"blocks,omitempty"`
BlocksSource string `json:"blocks_source"`
BlockSlugFiles []string `json:"block_slug_files,omitempty"`
SlugFile map[string]string `json:"-"`
// BlockDecls are the (slug, interfaceName) pairs §7.10 harvests, in scan
// order. The pair is the load-bearing fact: GraphQL publishes a blocks
// field's union as interfaceNames (CallToActionBlock) while the REST API
// only ever accepts the slug (cta), so neither half alone can turn the
// per-field union into something writable.
BlockDecls []BlockDecl `json:"block_decls,omitempty"`
// SlugByInterface is BlockDecls indexed by interfaceName. Blocks that
// declare no interfaceName are absent: there is nothing to key them by.
SlugByInterface map[string]string `json:"-"`
// FieldDocs are the per-field `admin: { description: '…' }` instructions
// declared by the project's COLLECTIONS and GLOBALS, keyed by entity slug
// and then by field name.
//
// They cover a collection's TOP-LEVEL `fields:` array only. A field nested
// inside a group, an array or a tab is not reached — the scanner adopts
// only the direct elements of the array it can attribute — and is reported
// as undocumented rather than being given a path this scanner would have
// had to guess.
//
// An entity whose config was read but documents nothing is present with an
// EMPTY map. The key's presence is the fact "PayCLI read this entity's
// config"; its absence is "there is none on disk", which is what a
// plugin-provided collection looks like.
FieldDocs map[string]map[string]FieldDoc `json:"field_docs,omitempty"`
// FieldDocFiles are the absolute paths FieldDocs were read from, keyed by
// entity slug, so the answer can always name its own source file.
FieldDocFiles map[string]string `json:"field_doc_files,omitempty"`
FilesScanned int `json:"files_scanned"`
BytesScanned int64 `json:"bytes_scanned"`
Truncated bool `json:"truncated,omitempty"`
}
ScanResult is everything §7.10 and §7.11 can learn from the project on disk. Every fact carries its provenance; nothing here is ever guessed.
func Scan ¶
func Scan(p *Project) *ScanResult
Scan reads what the local filesystem knows about the project: the Payload version from package.json (§7.11) and the block slugs from project source (§7.10).
It is local-filesystem only. It never makes a network call, never imports or evaluates project code, and never writes anything.
type UpdateSection ¶
type UpdateSection struct {
Auto *bool `toml:"auto,omitempty"`
Channel string `toml:"channel,omitempty"`
}
UpdateSection is the [update] table (§4.2).