Documentation
¶
Overview ¶
Package output owns PayCLI's single response envelope (§10) and every rendering of it.
The envelope is the contract with the agent calling PayCLI: `ok` is the one field to branch on, `data_kind` says what shape `data` has so nothing has to be guessed, `page.truncated` answers "did I get everything?" without arithmetic, and `next.cmd` is a literally runnable string with the current flags and profile already baked in.
Index ¶
- Constants
- Variables
- func CheckFormat(f Format, kind DataKind) error
- func EvalPath(data any, steps []pathStep) (any, error)
- func ParsePath(expr string) ([]pathStep, error)
- func SortedKeys(m map[string]any) []string
- type Alternative
- type CacheMeta
- type Changed
- type DataKind
- type EditOp
- type Edits
- type Envelope
- func (e *Envelope) AddWarning(w Warning) *Envelope
- func (e *Envelope) ApplyPath(expr string) error
- func (e *Envelope) Compact() ([]byte, error)
- func (e *Envelope) ExitCode() int
- func (e *Envelope) MarshalIndentTo() ([]byte, error)
- func (e *Envelope) NarrowedToScalar() bool
- func (e *Envelope) PropagateRawRedaction() *Envelope
- func (e *Envelope) RedactData() []string
- func (e *Envelope) WithChanged(c *Changed) *Envelope
- func (e *Envelope) WithEdits(ed *Edits) *Envelope
- func (e *Envelope) WithMeta(m Meta) *Envelope
- func (e *Envelope) WithNext(n *Next) *Envelope
- func (e *Envelope) WithPage(p *Page) *Envelope
- func (e *Envelope) WithRawBody(body []byte, noRedact bool) *Envelope
- func (e *Envelope) WithTarget(t *Target) *Envelope
- type ErrorsTo
- type Format
- type Locale
- type Meta
- type Next
- type Page
- type Renderer
- type RichTextMeta
- type Target
- type UpsertMeta
- type Warning
- type Writer
Constants ¶
const ( ReasonMorePages = "more_pages" ReasonRetryFailedSubset = "retry_failed_subset" ReasonNeedsAuth = "needs_auth" ReasonVerifyWrite = "verify_write" ReasonDiscoverFirst = "discover_first" ReasonRefreshDiscovery = "refresh_discovery" )
Next reasons (§10.1).
const ( AuthModeAPIKey = "api-key" AuthModeJWT = "jwt" AuthModeAnonymous = "anonymous" )
Auth modes reported in meta.auth_mode (§5.0).
const ( CacheHit = "hit" CacheStaleServed = "stale-served" CacheRevalidated = "revalidated" CacheMiss = "miss" CacheBypassed = "bypassed" CacheDisabled = "disabled" )
meta.cache.discovery states (§10.1).
const ( WarnRawRedacted = "raw_redacted" WarnDataRedacted = "data_redacted" WarnAnonymousSession = "anonymous_session" WarnIDTypeUnknown = "id_type_unknown" WarnOperatorPreviouslyFailed = "operator_previously_failed" WarnInputSilentlyDropped = "input_silently_dropped" WarnValueNormalizedByServer = "value_normalized_by_server" WarnCreatedAsDraft = "created_as_draft" )
Warning codes produced by the shared layers. Command-specific codes live with their commands.
const DefaultWidth = 100
DefaultWidth is the assumed terminal width when none was detected. 100 is wide enough for id + title + slug + status, which is what `pay find` prints.
const MaxRichTextPaths = 100
MaxRichTextPaths caps meta.richtext.paths.
const SchemaVersion = 1
SchemaVersion is envelope.v — the integer schema version of the envelope itself, bumped only on a breaking change to these keys.
Variables ¶
var DataKinds = []DataKind{ KindDoc, KindDocList, KindCount, KindGlobal, KindVersion, KindVersionList, KindBulkResult, KindCapabilities, KindSchema, KindCommandSpec, KindOpResult, KindRaw, KindError, }
DataKinds is the closed set, for validation and help.
var Formats = []Format{FormatJSON, FormatJSONL, FormatID, FormatRaw, FormatCSV, FormatTable}
Formats is the closed set, in help order.
Functions ¶
func CheckFormat ¶
CheckFormat returns format_unsupported (exit 5) naming the formats that DO work, so the caller's next attempt succeeds.
func EvalPath ¶
EvalPath applies a compiled expression to a value. The result REPLACES envelope.data; ok, v, command, data_kind, target, page, next, meta and warnings are preserved unchanged, so an agent can always still branch on .ok.
func ParsePath ¶
ParsePath compiles a --path expression. The returned steps are applied by EvalPath; an empty or "." expression is the identity.
func SortedKeys ¶
SortedKeys is a small helper shared by the csv and table renderers.
Types ¶
type Alternative ¶
Alternative is a cheaper or broader way to get the same answer.
type CacheMeta ¶
type CacheMeta struct {
Discovery string `json:"discovery"`
AgeS *int64 `json:"age_s"`
TTLS *int64 `json:"ttl_s"`
Fingerprint string `json:"fingerprint,omitempty"`
Revalidated bool `json:"revalidated"`
}
CacheMeta is meta.cache (§10.1).
type Changed ¶
type Changed struct {
Created int `json:"created"`
Updated int `json:"updated"`
Deleted int `json:"deleted"`
Trashed int `json:"trashed"`
IDs []any `json:"ids"`
}
Changed is the write summary (§10.2). Every counter is always present, even at zero, so an agent never has to presence-check one.
type DataKind ¶
type DataKind string
DataKind names the shape of Envelope.Data (§10.1).
const ( KindDoc DataKind = "doc" KindDocList DataKind = "doc_list" KindCount DataKind = "count" KindGlobal DataKind = "global" KindVersion DataKind = "version" KindVersionList DataKind = "version_list" KindBulkResult DataKind = "bulk_result" KindCapabilities DataKind = "capabilities" KindSchema DataKind = "schema" KindCommandSpec DataKind = "command_spec" KindOpResult DataKind = "op_result" KindRaw DataKind = "raw" KindError DataKind = "error" )
type EditOp ¶ added in v0.2.0
type EditOp struct {
// Command is the Cmd* constant of the stage that produced it.
Command string `json:"command"`
// Field is the document field it edited.
Field string `json:"field"`
// Detail is one human-readable sentence: "moved id:a1 (cta) from 0 to 2".
Detail string `json:"detail"`
// Matched are the source indices the selector resolved to, recorded
// because the indices shift under the next stage and this is the only
// record of what the caller actually addressed.
Matched []int `json:"matched,omitempty"`
// Rows is the row count after the op, so a listing is not needed to see
// that a remove removed something.
Rows int `json:"rows"`
}
EditOp is one applied transform.
type Edits ¶ added in v0.2.0
type Edits struct {
// Fields are the document's top-level field paths the pipeline changed, in
// first-touched order and deduplicated. `apply` builds its PATCH body from
// exactly these.
Fields []string `json:"fields"`
// Ops is every transform applied, oldest first, so the envelope explains
// itself after four stages of a pipe.
Ops []EditOp `json:"ops"`
}
Edits is the provenance a local transform stamps on its envelope, and the only thing that makes `pay get | pay blocks … | pay apply` safe.
Without it `apply` would have to send the whole document back, which means PATCHing every field PayCLI happened to read — including the server-owned ones (`createdAt`, `_status`) and any relationship the read expanded into an object. `Fields` narrows the write to the paths the pipeline actually touched, so a pipeline that moved one block writes one key.
It is present only on the local-edit commands and on `apply`; a command that talks to Payload never sets it.
type Envelope ¶
type Envelope struct {
OK bool `json:"ok"`
V int `json:"v"`
Command string `json:"command"`
DataKind DataKind `json:"data_kind"`
Partial bool `json:"partial,omitempty"`
Target *Target `json:"target,omitempty"`
Data any `json:"data,omitempty"`
Changed *Changed `json:"changed,omitempty"`
Edits *Edits `json:"edits,omitempty"`
Page *Page `json:"page,omitempty"`
Error *apierr.Error `json:"error,omitempty"`
Next *Next `json:"next,omitempty"`
Meta Meta `json:"meta"`
Warnings []Warning `json:"warnings"`
// RawBody is the server's response body for `--output raw`. It never
// appears in the JSON envelope; the writer emits it instead of the
// envelope. It is stored already redacted, with RawRedacted recording
// whether redaction changed the bytes so the writer can print the
// "--no-redact" notice on stderr (§10.3).
RawBody []byte `json:"-"`
RawRedacted bool `json:"-"`
// contains filtered or unexported fields
}
Envelope is the one object PayCLI prints. Struct order IS the JSON key order, which is §10.1's listing with `partial` (§12.5), `changed` (§10.2) and `error` (§11.1) folded into their natural neighbours: those three sections each show a subset and they do not agree on relative position, so the success listing in §10.1 wins and the extras sit next to what they describe.
func NewError ¶
NewError builds the error envelope (§11.1). Any error is accepted; one that never passed through apierr becomes `internal`, exit 1.
func (*Envelope) AddWarning ¶
AddWarning appends a warning. Warnings never change ok or the exit code.
func (*Envelope) Compact ¶
Compact renders the envelope on one line, which is what jsonl writes to stderr.
func (*Envelope) MarshalIndentTo ¶
MarshalIndentTo is the canonical JSON rendering: two-space indent and NO HTML escaping. Escaping matters: encoding/json turns "<" into "<" by default, which would mangle every "<redacted>" sentinel and every URL containing an ampersand in a where clause.
func (*Envelope) NarrowedToScalar ¶
NarrowedToScalar reports that --path reduced the envelope to a plain value (or a list of plain values) with no structure left.
It exists so `--output id` can print a path-selected scalar. The §10.3 matrix is keyed on data_kind, and data_kind describes what the COMMAND produced, so `pay doctor --path .ok --output id` — an example the help registry itself prints — failed with format_unsupported "--output id cannot render capabilities output", even though by then .data is just `true`. Nothing about a bare scalar is capabilities-shaped.
func (*Envelope) PropagateRawRedaction ¶
PropagateRawRedaction turns error.raw's redaction into the warnings[] entry §11.1 requires, so the agent knows the bytes differ from the wire.
func (*Envelope) RedactData ¶
RedactData masks every secret value inside .data (§5.3: "scrubs apiKey … from ALL output"), returning the paths that changed so the caller can say so in warnings[]. It is a no-op when nothing matched, and it is idempotent, so a command that already redacted its own payload pays nothing here.
The value is marshalled, spliced by redact.JSON and decoded back with UseNumber, which keeps a 19-digit document id and a trailing-zero decimal intact. Only a document that actually carried a secret is rebuilt.
func (*Envelope) WithChanged ¶
WithChanged sets the write summary.
func (*Envelope) WithEdits ¶ added in v0.2.0
WithEdits sets envelope.edits — the pipeline provenance a local transform carries to the next stage and, finally, to `pay apply`.
func (*Envelope) WithMeta ¶
WithMeta sets the per-invocation context, redacting base_url (§5.3 makes redaction of meta.base_url mandatory).
It is also where §5.0's `anonymous_session` warning is attached, because this is the ONE funnel meta.auth_mode passes through on its way into an envelope — success and error alike. Emitting it here rather than per command is what makes "every envelope carries it" true by construction instead of by thirty-odd remembered call sites; an anonymous run that silently reports a reduced permission matrix as if it were the whole project is a wrong answer, not a cosmetic omission.
func (*Envelope) WithRawBody ¶
WithRawBody attaches the server's body for `--output raw`, redacting it unless noRedact. When redaction changed the bytes the caller gets RawRedacted set, and the writer prints the one-line stderr notice naming --no-redact (§10.3).
func (*Envelope) WithTarget ¶
WithTarget sets envelope.target.
type ErrorsTo ¶
type ErrorsTo string
ErrorsTo selects which stream carries the error envelope (§11.1). The default is stdout — an agent reads one stream and branches on .ok — and PAY_ERRORS_TO=stderr / --errors-to stderr moves it for people who script around the Unix convention.
func ParseErrorsTo ¶
ParseErrorsTo validates --errors-to.
type Format ¶
type Format string
Format is the value of --output (§10.3).
const ( // FormatJSON is the DEFAULT: one pretty envelope object, for agents. FormatJSON Format = "json" // FormatJSONL streams bare documents on stdout, envelope on stderr. FormatJSONL Format = "jsonl" // FormatID prints one id per line and nothing else. FormatID Format = "id" // FormatRaw prints Payload's body with no envelope — after redaction, // unless --no-redact. FormatRaw Format = "raw" // FormatCSV is RFC 4180, for humans. FormatCSV Format = "csv" // FormatTable is aligned and width-truncated, for humans. Never // recommended to agents. FormatTable Format = "table" )
func ParseFormat ¶
ParseFormat validates --output. An unknown value is invalid_option (exit 5) with the valid list attached, never a silent fallback to json.
type Locale ¶
Locale carries the RESOLVED values PayCLI actually sent, so an agent can see that fallback "none" is why a field came back null. Both are null on a non-localised project (§10.1).
type Meta ¶
type Meta struct {
RequestID string `json:"request_id"`
CLIVersion string `json:"cli_version"`
Profile string `json:"profile"`
BaseURL string `json:"base_url"`
APIPath string `json:"api_path"`
AuthMode string `json:"auth_mode"`
PayloadVersion *string `json:"payload_version"`
PayloadVersionSource string `json:"payload_version_source"`
Locale Locale `json:"locale"`
DateField *string `json:"date_field"`
// SearchedFields is §9.4's mandatory report for --q: the text fields the
// OR of `contains` was actually built over. Absent on a command that did
// not search.
SearchedFields []string `json:"searched_fields,omitempty"`
Since *string `json:"since"`
Until *string `json:"until"`
DurationMS int64 `json:"duration_ms"`
HTTPRequests int `json:"http_requests"`
Retries int `json:"retries"`
Bytes int64 `json:"bytes"`
Cache *CacheMeta `json:"cache,omitempty"`
DiscoveryRevision string `json:"discovery_revision,omitempty"`
DryRun bool `json:"dry_run"`
// Backups lists the §12.8 backup files this invocation wrote before it
// changed or removed a document. Absent when nothing was backed up —
// backups are off, or the command changed no existing document.
Backups []string `json:"backups,omitempty"`
// RichText records that --richtext md|text rendered the rich text of the
// data for reading. Absent when rich text is the stored Lexical JSON (the
// default). Its presence is what `pay apply` and `--data @-` refuse: a
// rendered document must never be written back.
RichText *RichTextMeta `json:"richtext,omitempty"`
// ResetNested reports a --reset-nested write (§10.2.1): the carrier
// phases sent before the requested body, the block tables they cleared
// and whether the stored row read back clean. Absent on every other
// command.
ResetNested any `json:"reset_nested,omitempty"`
// Upsert is what `pay upsert` decided (F6): absent on every other command.
Upsert *UpsertMeta `json:"upsert,omitempty"`
// RestoredFrom is the backup file `pay backups restore` wrote back
// (§12.8.8); absent on every other command.
RestoredFrom string `json:"restored_from,omitempty"`
}
Meta is the per-invocation context. payload_version is null rather than a fabricated constant when it could not be determined, and payload_version_source always says which it is (§7.11).
type Next ¶
type Next struct {
Reason string `json:"reason"`
Cmd string `json:"cmd"`
Args map[string]any `json:"args,omitempty"`
Alternatives []Alternative `json:"alternatives,omitempty"`
}
Next is the follow-up PayCLI recommends. Cmd is literally runnable.
type Page ¶
type Page struct {
Limit int `json:"limit"`
Page int `json:"page"`
TotalPages int `json:"total_pages"`
TotalDocs int `json:"total_docs"`
Returned int `json:"returned"`
HasNextPage bool `json:"has_next_page"`
HasPrevPage bool `json:"has_prev_page"`
NextPage *int `json:"next_page"`
PrevPage *int `json:"prev_page"`
Truncated bool `json:"truncated"`
}
Page is present iff data_kind == "doc_list" (§10.1). Truncated is the one boolean that requires no arithmetic: true means more matching documents exist than this envelope holds (another page on a paginated read, or --all stopped at --max).
type Renderer ¶
Renderer writes one envelope to its destination and returns the process exit status implied by it. internal/cli/app.go is the only place that owns the process's real standard streams; everything else is handed a Renderer, which is what makes CLI-level golden testing possible at all.
type RichTextMeta ¶ added in v0.3.0
type RichTextMeta struct {
// Format is what every editor state was replaced by: "md" or "text".
Format string `json:"format"`
// Rendered counts the editor states replaced.
Rendered int `json:"rendered"`
// Paths are the replaced values' paths (`content`, `layout[2].richText`,
// `[0].content` in a list), at most MaxRichTextPaths of them.
Paths []string `json:"paths"`
// PathsTruncated reports that Paths was capped.
PathsTruncated bool `json:"paths_truncated,omitempty"`
}
RichTextMeta is meta.richtext.
type Target ¶
type Target struct {
Kind string `json:"kind"` // "collection" | "global"
Slug string `json:"slug"`
Singular string `json:"singular,omitempty"`
IDType string `json:"id_type,omitempty"`
ID any `json:"id,omitempty"`
}
Target names what the command acted on.
type UpsertMeta ¶ added in v0.3.0
type UpsertMeta struct {
// Action is created | updated | unchanged after a real run, and the
// plan vocabulary create | update | unchanged under --dry-run (nothing
// happened yet).
Action string `json:"action"`
// MatchedBy is the --match fields and the values taken from the body.
MatchedBy map[string]any `json:"matched_by"`
// ID is the matched (or created) document's id; null for a dry-run create.
ID any `json:"id"`
// Forced is true when --force wrote a body that changed nothing.
Forced bool `json:"forced,omitempty"`
}
UpsertMeta is meta.upsert.
type Warning ¶
type Warning struct {
Code string `json:"code"`
Message string `json:"message"`
Paths []string `json:"paths,omitempty"`
Sent map[string]any `json:"sent,omitempty"`
Returned map[string]any `json:"returned,omitempty"`
StillMissing []string `json:"still_missing,omitempty"`
Hint string `json:"hint,omitempty"`
}
Warning never changes ok or the exit code (§10.1).
func AnonymousSessionWarning ¶
AnonymousSessionWarning is §5.0's verbatim notice that no credential was resolved. The hint is a literally runnable login command for THIS profile.
func DataRedactedWarning ¶
DataRedactedWarning is the §10.3-style notice that .data differs from the bytes the server sent, naming the flag that turns the masking off.
func IDTypeUnknownWarning ¶
IDTypeUnknownWarning is §7.6(a)'s mandatory transparency notice: the client-side id check was SKIPPED because PayCLI never learned this collection's id type, so a malformed id will come back from the server rather than from PayCLI. The hint is the one from §7.6(a) verbatim, so this warning and `pay explain limitations` cannot drift apart.
func OperatorPreviouslyFailedWarning ¶
OperatorPreviouslyFailedWarning is §7.11's reactive memo: this operator has already failed on this collection, with the recorded evidence, and PayCLI is sending it anyway rather than inventing a refusal.
type Writer ¶
type Writer struct {
Stdout io.Writer
Stderr io.Writer
// Format is --output. Zero value renders as json.
Format Format
// ErrorsTo is --errors-to.
ErrorsTo ErrorsTo
// Columns overrides the default column selection for csv and table.
Columns []string
// Width is the terminal width for table output; 0 uses DefaultWidth.
Width int
// Human adds the one-line stderr summary on an error (§11.1). It is on by
// default; --quiet turns it off.
Quiet bool
// NoRedact is --no-redact. The zero value redacts, so a Writer built
// anywhere (including in a test) can never be the thing that spills a
// credential (§5.3).
NoRedact bool
// StdoutIsData says the command has already written raw bytes to Stdout
// and owns that stream (today: `pay download -o -`). The envelope is then
// rendered to Stderr, because appending JSON after a PNG produces a file
// that is neither: a 463-byte image came out as a 1570-byte hybrid. It is
// deliberately a writer-level flag rather than a per-command dance, so
// every future byte-streaming command gets the same protection.
StdoutIsData bool
}
Writer is the concrete Renderer.