Documentation
¶
Overview ¶
Package discovery builds PayCLI's adaptive capability manifest (§7).
The package exists because one binary has to drive *any* Payload 3.x project: slugs, GraphQL type names, id types, upload/versions/drafts/trash support and the queryable field set are all project-specific, and several of them are not obtainable at all from some deployments. Three rules therefore govern every line of it:
**Shape, never status.** Every REST probe validates the shape of the response body. A custom collection endpoint can shadow any built-in route, an unauthenticated /api/access answers 200 with a truncated view, and a wrong base URL answers 200 text/html.
**Tri-state, never defaulted** (§7.6). Every fact only GraphQL can establish is value | unknown, with a *_source sibling. An unknown value must never cause a local rejection: a defaulted id_type would make PayCLI reject valid Mongo ObjectIds with an error message that is a lie.
**Read-only by default** (§7.7). Nothing here creates a document unless the caller passed AllowWriteProbes, and §8.4's Level-3 reactive invalidation is never armed from this package — discovery deliberately generates every Level-3 trigger, so an ambient rule would recurse.
The package never imports the CLI layer or cobra, never reads a clock of its own (Options.Now is injected), and never writes to disk: it returns a typed Manifest plus field Shards and lets internal/cache persist them.
Index ¶
- Constants
- Variables
- func AttributeOperatorFailure(serverFailed bool, operators []string) (string, bool)
- func BlockSchemasForShard(shard *Shard, schema *Schema, src BlockSources) map[string]BlockTypeSchema
- func CrossValidateLocales(candidates, enumNames []string) bool
- func DescribeBlocksHelp(interfaces []string, profile, fieldPath string) string
- func FlagsFromObject(obj *IntroType) (drafts, trash, folders *bool)
- func FormatName(slug string) string
- func HashShard(s *Shard) string
- func HumanizeField(name string) string
- func IDString(v any) string
- func IDTypeFromArg(f *IntroField) (string, bool)
- func IDTypeOfJSON(v any) (string, bool)
- func InferDBAdapter(idTypes []string, sampleIDs []string) (adapter, source string)
- func IsComplexityError(msgs []string) bool
- func IsInternal(slug string) bool
- func IsJoinType(t *IntroType) bool
- func IsRelationshipType(t *IntroType) bool
- func IsUploadType(t *IntroType) bool
- func LocaleEnumNames(schema *Schema) []string
- func LocaleUnverifiedHint(profile string) string
- func LocalesValidatable(source string) bool
- func LooksNumeric(s string) bool
- func Mitigation(code string) string
- func ObservedBlockTypes(docs []map[string]any) map[string][]string
- func OperatorFailureCandidate(operator string) bool
- func OperatorsFor(payloadType, operatorTypeName string) []string
- func ParseDeleteMessage(message string) (string, bool)
- func ResolveIDType(in IDLadderInput) (idType, source string)
- func ResolveLocales(in LocaleInput) (locales []string, source string, count *int)
- func ResolveShardBlocks(shard *Shard, schema *Schema, src BlockSources)
- func RouteMissingHint(graphQLPath, apiPath, graphQLRoute, graphQLPathSource string) string
- func SchemaSHA256(queryFieldNames []string) string
- func SlugFromInterfaceName(name string) string
- func SortableKind(payloadType string) bool
- func TitleField(shard *Shard) *string
- func TitleFromSlug(slug string) string
- func TopologySHA256(p TopologyProjection) string
- func UnionMembers(schema *Schema, graphQLType *string) []string
- func UnresolvedBlocksReason(fieldPath string) string
- func UploadFromObject(obj *IntroType) *bool
- func UseAPIKey(input *IntroType) *bool
- func WriteShapeFor(payloadType string, polymorphic, hasMany bool) *string
- type AuthBootstrap
- type BlockDoc
- type BlockField
- type BlockFieldSchema
- type BlockResolution
- type BlockSourceDecl
- type BlockSourceField
- type BlockSources
- type BlockTypeSchema
- type Capabilities
- type Collection
- type Diagnostics
- type Discoverer
- type Field
- type FieldDoc
- type Fingerprint
- type Flags
- type FlagsSource
- type Global
- type GlobalFlags
- type GlobalFlagsSource
- type GlobalPermissions
- type GraphQLCapability
- type GraphQLNames
- type IDLadderInput
- type Identity
- type IntroArg
- type IntroEnumValue
- type IntroField
- type IntroInputField
- type IntroType
- type IntroTypeName
- type Jobs
- type Kind
- type Labels
- type LearnedFailure
- type Limitation
- type LocaleAnalysis
- type LocaleInput
- type Localization
- type Manifest
- func (m *Manifest) AddLimitation(code, entity, field, detail string)
- func (m *Manifest) AddUnreachable(slug, kind, reason, detail string)
- func (m *Manifest) Collection(slug string) (*Collection, bool)
- func (m *Manifest) CollectionSlugs() []string
- func (m *Manifest) Degrade(stage string)
- func (m *Manifest) Global(slug string) (*Global, bool)
- func (m *Manifest) GlobalSlugs() []string
- func (m *Manifest) LearnedOperatorFailure(operator, collection string) (LearnedFailure, bool)
- func (m *Manifest) RecordOperatorFailure(operator, collection, evidence string, at time.Time) bool
- func (m *Manifest) ResolveCollection(slug string) (*Collection, error)
- func (m *Manifest) ResolveGlobal(slug string) (*Global, error)
- func (m *Manifest) Revision() string
- func (m *Manifest) Sort()
- type ManifestMeta
- type Options
- type Permissions
- type Preferences
- type Result
- type Schema
- type Shard
- func (s *Shard) BlockFieldFor(path string) (BlockField, bool)
- func (s *Shard) BlockSchemaFor(slug string) (BlockTypeSchema, bool)
- func (s *Shard) BlockSlugsFor() []string
- func (s *Shard) BlockTypesFor(path string) ([]string, bool)
- func (s *Shard) DateFields() []string
- func (s *Shard) DocumentedPaths() []string
- func (s *Shard) Field(path string) (Field, bool)
- func (s *Shard) FieldDocFor(path string) (FieldDoc, bool)
- func (s *Shard) Finalize()
- func (s *Shard) Paths() []string
- func (s *Shard) QueryablePaths() []string
- func (s *Shard) SetBlockField(bf BlockField)
- func (s *Shard) SetFieldDocs(docs map[string]FieldDoc, file, source string)
- func (s *Shard) SortablePaths() []string
- type Source
- type Stats
- type TopologyProjection
- type TypeRef
- type Unreachable
- type UnsupportedOperator
Constants ¶
const ( KindScalar = "SCALAR" KindObject = "OBJECT" KindInterface = "INTERFACE" KindUnion = "UNION" KindEnum = "ENUM" KindInputObject = "INPUT_OBJECT" KindList = "LIST" KindNonNull = "NON_NULL" )
GraphQL introspection kinds.
const ( DBPostgres = "postgres" DBMongoDB = "mongodb" DBSQLite = "sqlite" DBUnknown = "unknown" )
DB adapter values (§7.11).
const ( // ReachabilityOK means both /api/access and the GraphQL Access type // listed the entity — or, when GraphQL is unavailable, that /api/access // listed it and nothing contradicted that. ReachabilityOK = "ok" // ReachabilityGraphQLDisabled means /api/access saw it and GraphQL did // not. ReachabilityGraphQLDisabled = "graphql-disabled" // ReachabilityAccessDenied means GraphQL saw it and /api/access did not, // i.e. this identity has zero permissions on it. ReachabilityAccessDenied = "access-denied" )
Reachability records which of the two inventory sources saw an entity (§7.3). Neither source alone is complete: /api/access omits payload-kv (zero permission for this identity) and GraphQL omits payload-migrations (endpoints: false), both verified live.
const ( ReasonAccessDenied = "access-denied" ReasonEndpointsDisabled = "endpoints-disabled" ReasonGraphQLDisabled = "graphql-disabled" )
Reasons recorded in manifest.unreachable[].
const ( GraphQLModeOK = "ok" GraphQLModeIntrospectionDisabled = "introspection_disabled" GraphQLModeErrors = "errors" GraphQLModeDisabled = "disabled" GraphQLModeRouteMissing = "route_missing" GraphQLModeUnreachable = "unreachable" )
GraphQL degradation modes (§7.6).
const ( SourceConfigured = "configured" SourceProbed = "probed" SourceGraphQL = "graphql" SourceGraphQLInput = "graphql-input" SourceGraphQLEnum = "graphql-enum" SourceGraphQLArg = "graphql-arg" // SourceGraphQLArgAbsent is §7.8.1's localization.locales_source for "the // plural Query field has no locale argument", i.e. the project is provably // not localised. SourceGraphQLArgAbsent = "graphql-arg-absent" SourceGraphQLEnumMangled = "graphql-enum-mangled" SourceProbe = "probe" SourceAccess = "access" SourceObserved = "observed" SourceLocaleAll = "locale-all" SourceLocaleAllUnverified = "locale-all-unverified" SourceProjectSource = "project-source" SourceProjectPackageJSON = "project-package-json" SourceBulkDeleteMessage = "bulk-delete-message" SourceDerived = "derived" SourceAccessZip = "access-zip" SourceTypeProbe = "type-probe" SourceFuzzy = "fuzzy" SourceBootstrap = "bootstrap" SourceCached = "cached" SourceInferred = "inferred" SourceWriteProbe = "write-probe" SourceUnknown = "unknown" // SourceNA is the documented sentinel for "this key does not apply to // this field", as opposed to "PayCLI could not find out". SourceNA = "n/a" // SourceDerivedGraphQLPath mirrors config.SourceDerivedGraphQL so the two // layers report the same string for the same fact. SourceDerivedGraphQLPath = "derived:api_path+graphql_route" )
Provenance values. Every derived fact is written together with the story of where it came from, or neither is written (§7.8.3).
const ( ConfidenceGraphQL = "graphql" ConfidenceInferred = "inferred" ConfidenceObserved = "observed" ConfidenceHeuristic = "heuristic" ConfidenceMeasured = "measured" ConfidenceUnknown = "unknown" )
Confidence values for payload_type and sortable.
const ( IDTypeNumber = "number" IDTypeString = "string" IDTypeUnknown = "unknown" )
IDType values (§7.6). Unknown is a first-class answer, not an error state.
const ( TypeText = "text" TypeTextarea = "textarea" TypeNumber = "number" TypeCheckbox = "checkbox" TypeDate = "date" TypeEmail = "email" TypeSelect = "select" TypeRadio = "radio" TypeRelationship = "relationship" TypeUpload = "upload" TypeJoin = "join" TypeBlocks = "blocks" TypeRichText = "richText" TypeJSON = "json" TypeGroup = "group" TypeArray = "array" TypePoint = "point" TypeID = "id" TypeUnknown = "unknown" )
Payload field kinds inferred by §7.4's table.
const ( WriteShapeID = "id" WriteShapeIDArray = "id_array" WriteShapeRelValue = "{relationTo,value}" WriteShapeRelList = "[{relationTo,value}]" WriteShapeJSON = "json" )
WriteShape is §7.8.2's closed enum. It is mandatory and non-null on every relationship, upload and polymorphic field and null on every other kind, because §9.10's --set coercion reads exactly this key.
const ( HookMutatedUnknown = "unknown" HookMutatedTrue = "true" HookMutatedFalse = "false" )
HookMutated is tri-state as a string because "unknown" is the normal answer: beforeChange / beforeValidate hooks are not introspectable (§7.6, HOOK_MUTATION_UNKNOWN).
const ( LimBlockSlugsUnknown = "BLOCK_SLUGS_UNKNOWN" LimCustomEndpointsNotEnum = "CUSTOM_ENDPOINTS_NOT_ENUMERABLE" LimSortabilityHeuristic = "SORTABILITY_HEURISTIC" LimLocalizationUnknown = "LOCALIZATION_UNKNOWN" LimRichTextShapeUnknown = "RICHTEXT_SHAPE_UNKNOWN" LimIDTypeUnknown = "ID_TYPE_UNKNOWN" LimCapabilityUnknown = "CAPABILITY_UNKNOWN" LimLocalesUnknown = "LOCALES_UNKNOWN" LimLocalizationPerFieldUnknwn = "LOCALIZATION_PER_FIELD_UNKNOWN" LimBlockSlugsUnknownNoSource = "BLOCK_SLUGS_UNKNOWN_NO_SOURCE" LimAuthCandidatesTruncated = "AUTH_CANDIDATES_TRUNCATED" LimHookMutationUnknown = "HOOK_MUTATION_UNKNOWN" LimPayloadVersionUnknown = "PAYLOAD_VERSION_UNKNOWN" LimDBAdapterUnknown = "DB_ADAPTER_UNKNOWN" )
Limitation codes (§7.6). They are stable identifiers: an agent branches on the code, never on the prose.
const ( StageModeProbe = "mode_probe" Stage1 = "stage1" Stage2 = "stage2" Stage3 = "stage3" StageFingerprint = "fingerprint" )
Stage names recorded in diagnostics.degraded[].
const BlockDocsNote = "label and description are the project's OWN words, read from the block's " +
"config.ts on disk (§7.10): `labels: { singular, plural }` and a description under " +
"custom.description / custom.docs / custom.summary. Payload publishes neither over REST or " +
"GraphQL and defines NO description field for a block at all, so a project gets these only if " +
"its authors wrote them; null means nobody did, and description_key says which key answered."
BlockDocsNote states, once per response, where a block's label and description come from — so that a null is read as "this project did not write one" and never as "PayCLI failed".
const BlockPlumbingNote = "id, blockName and blockType are Payload plumbing, not content fields: " +
"blockType is MANDATORY on every block you write and must be the slug (Payload silently drops a " +
"row whose blockType it does not recognise and still answers 201), blockName is an optional " +
"admin-UI label, and id is server-generated — omit it when creating."
BlockPlumbingNote is the one-line explanation of the three keys Payload puts on every block row. They are protocol, not content, and an agent that treats them as fields to fill in produces nonsense.
const DefaultTTL = 10 * time.Minute
DefaultTTL is the discovery manifest's fresh window (§8.5).
const FallbackNone = "none"
FallbackNone is the value PayCLI sends as fallback-locale on every read when localisation is enabled (§7.9a). sanitizeFallbackLocale maps 'false' | 'none' | 'null' to false, so an untranslated field comes back null/absent instead of masquerading as a translation.
This is not a display preference. config.localization.fallback defaults to true, so without it `pay get pages 1 --locale de` returns English strings for every untranslated field, and an agent that read-modify-writes copies English into de and believes it translated the page.
const LocaleUnverifiedMessage = "" /* 203-byte string literal not displayed */
LocaleUnverifiedMessage is §7.9c's message, verbatim. It is verbatim because the behaviour it describes is invisible: an unknown locale code is silently coerced to the default locale with HTTP 200 and no warning.
const LocaleWarnUnverified = "locale_unverified"
LocaleWarnUnverified is the warning code attached when --locale could not be validated client-side.
const ManifestVersion = 2
ManifestVersion is §7.8.1's layout version. It is 2 because the single consolidated manifest of the first draft was split into an index plus field shards; a manifest_version: 1 file on disk is a cache miss, not a parse error.
const MaxLearnedOperatorFailures = 64
MaxLearnedOperatorFailures bounds the memo. It is a cache file an agent may never garbage-collect by hand, and a project with more than this many distinct failing operator/collection pairs has a bigger problem than the memo can express; the oldest record is dropped first.
const SourceMixed = "mixed"
SourceMixed is a field whose slugs did not all come from the same place. It never appears alone: SlugSources always says which slug came from where.
const SourcePayloadProtocol = "payload-protocol"
SourcePayloadProtocol is the provenance of a fact that comes from Payload's block wire format itself rather than from this project: every block row carries id/blockName/blockType, and blockType must be sent. It is a distinct source because it is true of every Payload 3.x project and was not measured on this one.
const SourceUnionInferred = "inferred-from-interface-name"
SourceUnionInferred is the provenance of a slug derived from a GraphQL interfaceName because the project source had no pair for it — which is how every plugin-provided block arrives, since node_modules is deliberately not scanned. It is deliberately distinct from SourceProjectSource so an agent can tell a confirmed slug from an inferred one at a glance.
Variables ¶
var BlockFieldSchemaKeys = []string{
"name", "path", "parent",
"payload_type", "payload_type_confidence", "payload_type_source",
"graphql_type", "json_type", "has_many",
"required", "required_source",
"options", "options_source",
"relation_to", "relation_to_source", "polymorphic",
"write_shape",
"plumbing",
"label",
"description", "description_source", "description_key",
}
BlockFieldSchemaKeys is the exact key set every block field entry carries, exported so a consumer can assert the contract instead of re-deriving it.
var FieldKeys = []string{
"name", "path", "graphql_path", "parent",
"payload_type", "payload_type_confidence",
"graphql_type", "json_type",
"required", "required_source",
"has_many", "localized", "localized_source",
"read_only",
"options", "options_source",
"relation_to", "relation_to_source", "polymorphic",
"write_shape",
"queryable", "operators",
"sortable", "sortable_confidence",
"hook_mutated",
"label",
}
FieldKeys is the exact, ordered key set every field entry carries. It is exported so a consumer can assert the contract instead of re-deriving it.
Functions ¶
func AttributeOperatorFailure ¶
AttributeOperatorFailure implements the test described above. It returns the operator to blame and true only when the failure is genuinely attributable.
serverFailed is the caller's verdict that the server, not PayCLI, refused the request: an HTTP 500 or a GraphQL QueryError. operators is every distinct operator the request carried.
func BlockSchemasForShard ¶ added in v0.2.0
func BlockSchemasForShard(shard *Shard, schema *Schema, src BlockSources) map[string]BlockTypeSchema
BlockSchemasForShard builds one schema per distinct blockType slug reachable from any of the shard's blocks fields.
Keyed by SLUG because that is what an agent writes, and because a block type used by two fields is the same type both times: verified that pages.layout and posts.layout share cta, content and mediaBlock.
func CrossValidateLocales ¶
CrossValidateLocales is §7.9b step 2's GraphQL check: accept the locale=all candidates only when their count matches LocaleInputType's and every candidate formatName()s to an enum name.
func DescribeBlocksHelp ¶
DescribeBlocksHelp is the text `pay describe <c> --field <f>` prints for an unresolved blocks field, verbatim from §7.10. It names only sources that can actually answer: no hint in PayCLI may name a `pay` command that cannot.
func FlagsFromObject ¶
FlagsFromObject reads the three capability flags that Payload encodes as fields of the object type. This is strictly better evidence than §7.5's REST probes — it is the schema rather than an inference from a 400 — and it is what keeps §7.1's four-request cold budget intact on a project whose GraphQL is reachable.
_status => drafts are enabled (verified: Page has it, Category does not) deletedAt => trash is enabled (verified: CrmContact has it, Page does not) folder => folders are enabled (verified: Media has it, Page does not)
func FormatName ¶
FormatName reproduces Payload's formatName(): every character in -./+,()'[] and space becomes an underscore. It is what turns the slug `crm-contacts` into the Access field `crm_contacts`, and it is the only direction that is safe to compute — the inverse is ambiguous.
func HashShard ¶
HashShard is the content hash written to fields_sha256. The hash covers the fields and their provenance but not the generation or the hash slot itself, so an unchanged schema keeps a stable hash across runs and §8.4's staleness check cannot be defeated by a new generation id.
func HumanizeField ¶
HumanizeField turns a camelCase or snake_case field name into an admin-ish label. It is a display aid only: Payload's real labels are not introspectable per field, and nothing behavioural keys off this string.
func IDString ¶
IDString renders an observed id for the adapter inference without going through fmt, so a json.Number keeps its exact digits.
func IDTypeFromArg ¶
func IDTypeFromArg(f *IntroField) (string, bool)
IDTypeFromArg reads §7.3's authoritative signal: the `id` argument of the singular Query field. Int means a relational adapter's integer primary key, String means Mongo ObjectIds or UUIDs.
This matters beyond validation: passing a non-castable id causes 500s, and on /duplicate it caused unintended writes.
func IDTypeOfJSON ¶
IDTypeOfJSON types a decoded JSON id value. json.Number and float64 are both accepted because a decoder configured with UseNumber and one without must agree.
func InferDBAdapter ¶
InferDBAdapter is §7.11 source 3: the only inference the API supports, and it is deliberately weak. It never unlocks §9.3's client-side unsupported_operator block, which requires db_adapter_source == "configured" — because on MongoDB `all` maps to $all and works, so a guessed adapter would make PayCLI refuse a query the server would have answered.
idTypes are the resolved id_types of every collection; sampleIDs are any observed string ids.
func IsComplexityError ¶
IsComplexityError reports §7.4's halve-the-batch trigger.
func IsInternal ¶
IsInternal reports Payload's own collections, which §2's ground truth says to de-emphasise in default listings but keep reachable.
func IsJoinType ¶
IsJoinType reports §7.4's "OBJECT with exactly {docs, hasNextPage, totalDocs}". The check is on the key set, not on the type name, because the name is {T}_{field} and a group field can share that shape of name.
func IsRelationshipType ¶
IsRelationshipType reports §7.4's polymorphic wrapper: an OBJECT whose only fields are relationTo and value.
func IsUploadType ¶
IsUploadType reports whether an OBJECT type carries all four upload markers.
func LocaleEnumNames ¶
LocaleEnumNames reads LocaleInputType's values. They are formatName()-mangled (en-US is exposed as en_US) and introspection never exposes the underlying value, so they are used for cross-validation and for locale_count only — never as codes (§7.9).
func LocaleUnverifiedHint ¶
LocaleUnverifiedHint is §7.9c's hint, verbatim modulo the profile name.
func LocalesValidatable ¶
LocalesValidatable reports §7.9c: --locale is checked client-side only when the codes are configured or were cross-validated from a locale=all sample. Anything else is passed through with the locale_unverified warning.
func LooksNumeric ¶
LooksNumeric reports whether a raw command-line id is all digits, used by callers that must decide whether a string id is safe to send to a number-typed collection.
func Mitigation ¶
Mitigation returns §7.6's recorded mitigation for a limitation code.
func ObservedBlockTypes ¶ added in v0.2.0
ObservedBlockTypes harvests the blockType values actually present in sampled documents, keyed by the dotted field path they appeared at.
This is the only source that is both per-field and evidence-based, so it is what keeps the REST-only path (no GraphQL, therefore no union) from having to guess. It reports what was SEEN, never what is allowed: the caller must not present it as a complete list.
func OperatorFailureCandidate ¶
OperatorFailureCandidate reports whether an operator is one §7.11 will ever learn about. Callers use it to skip the memo lookup for the ninety per cent of queries that only use equals/greater_than/contains.
func OperatorsFor ¶
OperatorsFor returns the operators Payload accepts for a field kind. A polymorphic relationship's where entry is a {T}_{f}_Relation input object taking relationTo and value rather than an operator set, which is why the operator type name is inspected as well as the kind.
func ParseDeleteMessage ¶
ParseDeleteMessage extracts the plural label from a bulk-delete message. The second result is false for a translated, reworded or custom message.
func ResolveIDType ¶
func ResolveIDType(in IDLadderInput) (idType, source string)
ResolveIDType walks §7.6's ladder, stopping at the first hit. The last rung is "unknown" — reachable on purpose, because a defaulted "number" would make PayCLI reject every valid 24-hex ObjectId with an error message that is a lie, and a defaulted "string" would silently lose the Postgres protection the check exists for.
func ResolveLocales ¶
func ResolveLocales(in LocaleInput) (locales []string, source string, count *int)
ResolveLocales walks §7.9b, stopping at the first hit. The mangled GraphQL enum names are deliberately not returned as codes: en-US is exposed as en_US and es.419 as es_419, and introspection never exposes the underlying value, so codes harvested that way would be rejected by PayCLI's own validation if fed back.
func ResolveShardBlocks ¶ added in v0.2.0
func ResolveShardBlocks(shard *Shard, schema *Schema, src BlockSources)
ResolveShardBlocks resolves EVERY blocks field of a shard, one field at a time, and records the answer on the shard.
The per-field union is read from the field's own GraphQL type (Page_Layout, Form_Fields, …) rather than from a project-wide list, which is the whole point: the project source scan cannot know which collection a block belongs to, so attaching its bag to every blocks field made `pay describe forms` advertise page layout blocks for a form-builder field.
It is idempotent and safe to run over an already-resolved shard; the caller must call Finalize afterwards, because the answer is part of the shard hash.
func RouteMissingHint ¶
RouteMissingHint is §7.6's path-specific hint. It fires only when the mode is route_missing, graphql_path was derived rather than set, and api_path is not the default — the exact situation in which a working GraphQL endpoint is being missed.
func SchemaSHA256 ¶
SchemaSHA256 is §8.4's Level-2 fingerprint: sha256 over the sorted field names of {__type(name:"Query"){fields{name}}}. It is byte-identical authenticated and unauthenticated, which makes it the only permission-free schema-version proxy the API offers.
func SlugFromInterfaceName ¶ added in v0.2.0
SlugFromInterfaceName runs Payload's GraphQL type naming backwards for a block whose slug PayCLI could not read from project source.
Payload names a block's union member after its interfaceName when one is declared and after the PascalCased slug otherwise, so lower-casing the first letter recovers the slug for every single-word and camelCase slug. Verified against @payloadcms/plugin-form-builder, whose nine Form_Fields members Checkbox, Country, Email, Message, Number, Select, State, Text and Textarea invert to exactly the nine slugs it declares (checkbox, country, email, message, number, select, state, text, textarea), and against a live form document whose fields[].blockType values are text, email and textarea.
It refuses the cases it cannot invert rather than guessing: an acronym run (FAQBlock) would yield fAQBlock and a kebab-case slug's PascalCase is not recoverable at all. Returning "" there makes the member show up in BlockResolution.Unresolved, which is the honest answer.
func SortableKind ¶
SortableKind reports §7.5's sortability heuristic. It is a heuristic, not a measurement, which is exactly why every field records sortable_confidence: "heuristic" and the manifest carries SORTABILITY_HEURISTIC.
func TitleField ¶
TitleField picks the field `pay find` shows as a document's human label. It is a display aid with no behavioural consequence, so a heuristic is honest here in a way it would not be for a capability.
func TitleFromSlug ¶
TitleFromSlug turns a slug into a title-cased label.
func TopologySHA256 ¶
func TopologySHA256(p TopologyProjection) string
TopologySHA256 is §8.4's Level-1 fingerprint. It detects a collection or global being added or removed, versions being enabled (readVersions appears) and access-control changes for this identity — for ~14 ms and 30 KB.
It cannot detect a field being added to an existing collection, because `fields` collapses to the boolean true for a privileged key. That blind spot is stated honestly here and is what Level 3 exists for.
func UnionMembers ¶ added in v0.2.0
UnionMembers returns the possibleTypes of a field's GraphQL type when that type is a union, and nil otherwise. nil means "the API published no union for this field", which is a genuinely different answer from "the union is empty" and is what sends ResolveBlocks down its non-GraphQL ladder.
func UnresolvedBlocksReason ¶
UnresolvedBlocksReason is §7.10's publishable_reason, verbatim.
func UploadFromObject ¶
UploadFromObject reports §7.4's upload signal on the entity's own type.
func UseAPIKey ¶
UseAPIKey reports §7.3's use_api_key signal: the mutation input type carries both apiKey and enableAPIKey (verified live on mutationUserInput). It drives §5.4's `pay doctor` reporting and nothing else.
func WriteShapeFor ¶
WriteShapeFor implements §7.8.2's closed enum. It is the only producer of the key, so a relationship can never be written without one.
Types ¶
type AuthBootstrap ¶
type AuthBootstrap struct {
// Collection is the resolved auth-collection slug.
Collection string
// Source is "bootstrap" when Stage -1 ran, "configured" when it was
// skipped because the slug was set, "cached" when it came from
// auth-resolution.json.
Source string
// Candidates are every slug that survived the /init filter, in the order
// they were tried. They are recorded in diagnostics.auth_candidates[].
Candidates []string
// Truncated reports that the candidate set was the anonymous slug list
// alone.
Truncated bool
// Requests is the number of HTTP requests Stage -1 spent.
Requests int
}
AuthBootstrap is Stage -1's result (§7.0).
type BlockDoc ¶ added in v0.2.0
type BlockDoc struct {
Slug string `json:"slug"`
Label *string `json:"label"`
LabelPlural *string `json:"label_plural"`
// LabelsSource / DescriptionSource are "project-source" or "unknown".
// Nothing here is ever derived from the slug: a block whose author wrote
// no labels has none, and title-casing "mediaBlock" would invent one.
LabelsSource string `json:"labels_source"`
Description *string `json:"description"`
DescriptionSource string `json:"description_source"`
// DescriptionKey names the config key the text came from, because Payload
// defines none for a block: see BlockTypeSchema.DescriptionKey.
DescriptionKey string `json:"description_key"`
// FieldsCount is the block's content fields, excluding the three plumbing
// keys every block row carries.
FieldsCount int `json:"fields_count"`
// DocsReason is empty exactly when both label and description were found,
// and otherwise says why they were not — a plugin block has no config on
// disk, and that is expected rather than a failure.
DocsReason string `json:"docs_reason"`
}
BlockDoc is the CHOOSING view of one block type: the human name, what the block is for, and how many fields it has — enough to pick between cta, content and mediaBlock without running a second command, and small enough to print beside every slug list.
It is a projection of BlockTypeSchema, never a second source of truth: every value here is copied from the schema that `--block <slug>` prints in full.
type BlockField ¶ added in v0.2.0
type BlockField struct {
Path string `json:"path"`
// Slugs are the blockType values the REST API accepts here, or null when
// none could be resolved.
Slugs []string `json:"slugs"`
// Source is the weakest provenance among Slugs, or "mixed".
Source string `json:"source"`
// SlugSources gives every slug its own provenance so a confirmed slug is
// distinguishable from one inferred from an interfaceName.
SlugSources map[string]string `json:"slug_sources"`
// SlugInterfaces pairs each slug with the GraphQL union member it came
// from, which is how a slug is joined to the block's field schema in
// Shard.BlockSchemas.
SlugInterfaces map[string]string `json:"slug_interface_names"`
// InterfaceNames is the field's GraphQL union possibleTypes, verbatim.
// They are NOT blockType slugs and must never be sent to the API.
InterfaceNames []string `json:"interface_names"`
// Unresolved lists union members no source could name; the field accepts
// them but PayCLI cannot say what to call them.
Unresolved []string `json:"unresolved_interface_names"`
// Reason is empty exactly when every slug came from a confirmed source.
Reason string `json:"reason"`
}
BlockField is one blocks field's resolved answer, recorded per field because there is no such thing as a project-wide blockType list: verified live that Page.layout accepts 5 block types and Form.fields accepts 9 entirely different ones.
type BlockFieldSchema ¶ added in v0.2.0
type BlockFieldSchema struct {
Name string `json:"name"`
// Path is dotted WITHIN the block: "links.link.url". It is not a
// collection field path and cannot be used in --where.
Path string `json:"path"`
Parent *string `json:"parent"`
PayloadType string `json:"payload_type"`
PayloadTypeConfidence string `json:"payload_type_confidence"`
// PayloadTypeSource is "graphql" unless the block's config.ts resolved an
// ambiguity GraphQL cannot: lexical richText and json are the same JSON
// scalar, and select and radio compile to the same ENUM.
PayloadTypeSource string `json:"payload_type_source"`
GraphQLType *string `json:"graphql_type"`
JSONType string `json:"json_type"`
HasMany bool `json:"has_many"`
// Required is tri-state and is null far more often here than on a
// collection field: see this file's header for why.
Required *bool `json:"required"`
RequiredSource string `json:"required_source"`
Options []string `json:"options"`
OptionsSource string `json:"options_source"`
RelationTo []string `json:"relation_to"`
RelationToSource string `json:"relation_to_source"`
Polymorphic bool `json:"polymorphic"`
WriteShape *string `json:"write_shape"`
// Plumbing marks id/blockName/blockType — Payload's own keys, not this
// block's content.
Plumbing bool `json:"plumbing"`
Label string `json:"label"`
// Description is the field's own human instruction in the project's own
// words, read from `admin: { description: '…' }` in the block's config.ts
// (§7.10). It is a POINTER: null is "nobody wrote one", which is a
// different fact from an empty string and must never be filled in with a
// sentence PayCLI made up.
Description *string `json:"description"`
// DescriptionSource is "project-source" when Description is non-null and
// "unknown" when it is null. A plugin's block lives in node_modules, which
// §7.10 never scans, so every one of its fields reads "unknown" — expected,
// and stated rather than hidden.
DescriptionSource string `json:"description_source"`
// DescriptionKey is the literal config key the text came from
// ("admin.description"), "" when there is none. Payload has no standard
// description for a BLOCK, so naming the key is what keeps this output
// honest about where the words originated.
DescriptionKey string `json:"description_key"`
}
BlockFieldSchema is ONE field inside a block type.
It is deliberately not §7.8.2's Field: `queryable`, `operators`, `sortable` and `localized` are measured against a collection's `{Singular}_where` and a `locale=all` sample, neither of which exists for a block's interior, and emitting them here would mean publishing four confident-looking values that were never measured.
type BlockResolution ¶
type BlockResolution struct {
// Slugs are the blockType values the REST API accepts for this field, or
// nil when nothing could resolve them. nil and empty mean different
// things: nil is "PayCLI does not know".
Slugs []string
// Source is how THIS field was resolved: the weakest provenance among its
// slugs, so a single inferred member downgrades the whole field.
Source string
// SlugSources gives each returned slug its own provenance, so an agent can
// tell a slug confirmed from project source apart from one inferred from
// an interfaceName.
SlugSources map[string]string
// SlugInterfaces pairs each slug with the GraphQL union member it was
// resolved from. It is what makes a block's own field schema reachable:
// the fields are published under the interfaceName (CallToActionBlock)
// while everything writable is keyed by the slug (cta), and without the
// pair the two halves cannot be joined.
SlugInterfaces map[string]string
// Interfaces is the field's GraphQL union possibleTypes, verbatim. They
// are reported, never returned as slugs: feeding an interfaceName back to
// the API is exactly the silent-wrong-answer this type exists to prevent.
Interfaces []string
// Unresolved are union members no source could name, i.e. slugs that are
// missing from Slugs even though the API accepts them.
Unresolved []string
// Reason is the plain-words explanation of why the answer is less than
// confirmed. It is empty only when every slug came from a confirmed
// source; when Slugs is nil it is §7.10's publishable_reason.
Reason string
}
BlockResolution is the answer for ONE blocks field. Every answer is per-field: there is no such thing as a project-wide blockType list, and pretending otherwise is what made `pay describe forms` advertise page layout blocks for a form-builder field.
func ResolveBlocks ¶
func ResolveBlocks(fieldPath string, interfaces []string, src BlockSources) BlockResolution
ResolveBlocks answers ONE blocks field.
interfaces are that field's union possibleTypes (interfaceNames) and are the AUTHORITATIVE SET of what the field accepts — Page_Layout has five members and Form_Fields has nine completely different ones, so no project-wide list can be correct for both. The order is:
- the profile's pin for this field path
- the field's own GraphQL union, each member resolved to a slug
- blockType values observed in this field's sampled documents
- unknown, with a reason
type BlockSourceDecl ¶ added in v0.2.0
type BlockSourceDecl struct {
File string
Fields []BlockSourceField
Complete bool
// LabelSingular and LabelPlural are the block's `labels:` literals. They
// are the human name of the block — "Call to Action" for slug cta — and
// are "" when the project declares none, never a title-cased guess.
LabelSingular string
LabelPlural string
// Description is what this block is FOR, in the project's own words, and
// DescriptionKey is the config key it was read from. Payload defines NO
// standard description for a block, so the key is always reported with the
// text: see config.DescriptionKeys.
Description string
DescriptionKey string
}
BlockSourceDecl is one block's declaration as it exists on disk.
Complete is the load-bearing flag: CallToAction's fields array contains `linkGroup({…})`, a helper call the scanner cannot expand, so `links` is absent from Fields even though the block has it. Only a Complete declaration may answer "false" for a field it does not list; an incomplete one answers "unknown", which is the difference between a fact and a guess.
func (BlockSourceDecl) Field ¶ added in v0.2.0
func (d BlockSourceDecl) Field(name string) (BlockSourceField, bool)
Field returns the declaration for a field name.
type BlockSourceField ¶ added in v0.2.0
type BlockSourceField struct {
Name string
Type string
Required *bool
// Description is the field's own `admin: { description: '…' }` — the
// per-field instruction an agent needs while filling that field in
// ("Pass a media document id"). "" is "the project's authors wrote none".
Description string
// DescriptionKey is the literal config path the text came from,
// "admin.description" in the normal case.
DescriptionKey string
}
BlockSourceField is one field declaration read out of a block's config.ts. Required is nil when the literal was absent or not a boolean.
type BlockSources ¶
type BlockSources struct {
// Configured is the profile's blocks map, keyed by field path. A pin is an
// explicit statement by the operator and outranks everything.
Configured map[string][]string
// ProjectSource is §7.10's local filesystem scan result — every slug:
// literal found in an object that also has fields:. It is a PROJECT-WIDE
// bag with no field attribution, so it is never the answer for a field on
// its own; it only supplies the vocabulary SlugByInterface indexes.
ProjectSource []string
// ProjectSourceFiles are the absolute paths each slug came from, recorded
// in diagnostics.block_slug_files[].
ProjectSourceFiles []string
// SlugByInterface maps an interfaceName declared in project source to the
// slug declared beside it in the same object literal. This is what turns a
// per-field union member into a writable blockType.
SlugByInterface map[string]string
// Observed are blockType values seen in sampled documents, keyed by field
// path. On a fresh project this yields nothing — verified that all 12 live
// pages have layout: [].
Observed map[string][]string
// SourceDecls are the block declarations §7.10's scan read off disk,
// keyed by slug. They carry the one fact GraphQL cannot supply for a block
// — `required: true` — because Payload publishes no input type for a block
// type. A slug absent from this map is a block PayCLI has no source for,
// which is the normal case for every plugin-provided block.
SourceDecls map[string]BlockSourceDecl
}
BlockSources are the candidate origins of a blocks field's real blockType slugs, in §7.10's resolution order.
GraphQL is authoritative about WHICH blocks a field accepts and useless about what they are CALLED on the wire: verified live that __type(name:"Page_Layout").possibleTypes is exactly CallToActionBlock, ContentBlock, MediaBlock, ArchiveBlock, FormBlock while the slugs the REST API accepts are cta, content, mediaBlock, archive, formBlock. The project's own source has the other half — src/blocks/CallToAction/config.ts declares slug: 'cta' beside interfaceName: 'CallToActionBlock' — so the two are only useful together.
type BlockTypeSchema ¶ added in v0.2.0
type BlockTypeSchema struct {
Slug string `json:"slug"`
// InterfaceName is the GraphQL union member this schema was read from. It
// is NOT writable — sending it as a blockType is the §7.10 mistake.
InterfaceName string `json:"interface_name"`
// Fields are the block's own fields plus its three plumbing keys, in
// GraphQL order, with nested group/array interiors flattened into dotted
// paths.
Fields []BlockFieldSchema `json:"fields"`
// FieldsSource is "graphql" when the object type was readable and
// "unknown" when it was not.
FieldsSource string `json:"fields_source"`
// RequiredSource is the summary across the block's content fields: the one
// source when every field agreed, "mixed" when they did not (including
// when SOME field is unknown), and "unknown" when nothing answered for any
// of them. RequiredUnknown and Reason name the gaps precisely.
RequiredSource string `json:"required_source"`
// RequiredUnknown names every content field whose required is null, so an
// agent can see the gap without walking the list.
RequiredUnknown []string `json:"required_unknown"`
// ConfigFile is the absolute path required-ness was read from, or "" when
// no project source declared this slug.
ConfigFile string `json:"config_file"`
// PlumbingNote explains id/blockName/blockType in one line.
PlumbingNote string `json:"plumbing_note"`
// Reason is empty exactly when every content field's required-ness is
// known; otherwise it says in plain words what is missing and why.
Reason string `json:"reason"`
// Label and LabelPlural are the block's human name, read from its
// `labels: { singular, plural }` (§7.10). They are POINTERS because a
// project that declares none has none: null is the honest answer, and
// title-casing the slug would manufacture a label the admin UI never
// shows.
Label *string `json:"label"`
LabelPlural *string `json:"label_plural"`
// LabelsSource is "project-source" when the labels were read off disk and
// "unknown" when they were not.
LabelsSource string `json:"labels_source"`
// Description is the one-sentence statement of what this block is FOR —
// the fact that lets an agent choose between cta, content and mediaBlock
// without opening the project. null when the project's authors wrote none.
Description *string `json:"description"`
// DescriptionSource is "project-source" or "unknown".
DescriptionSource string `json:"description_source"`
// DescriptionKey is the config key the text was read from —
// "custom.description", "custom.docs" or "custom.summary". It is reported
// because Payload defines NO description for a block: a Block's `admin`
// accepts only components/custom/disableBlockName/group/images/jsx and
// `tsc --noEmit` rejects `admin: { description }` on one, so the text is
// always project convention rather than a standard field, and saying which
// key it came from is the difference between reporting and implying.
DescriptionKey string `json:"description_key"`
// DocsReason is empty exactly when both the label and the description were
// found; otherwise it says in plain words which is missing and why.
DocsReason string `json:"docs_reason"`
}
BlockTypeSchema is one block type's full answer: the slug to write, the GraphQL union member it came from, and every field inside it.
func BuildBlockSchema ¶ added in v0.2.0
func BuildBlockSchema(slug, iface string, schema *Schema, decl BlockSourceDecl, declared bool) BlockTypeSchema
BuildBlockSchema reads ONE block type's field schema from its GraphQL object type, overlaying what the project's own source says about required-ness.
iface is the union member name (CallToActionBlock); slug is what the REST API accepts (cta). Both are needed: the schema is read under one name and written under the other.
func (BlockTypeSchema) ContentFields ¶ added in v0.2.0
func (s BlockTypeSchema) ContentFields() []BlockFieldSchema
ContentFields returns the block's fields minus Payload's plumbing.
func (BlockTypeSchema) DisplayName ¶ added in v0.2.0
func (s BlockTypeSchema) DisplayName() string
DisplayName is the one-line human name for a block: its declared singular label, or the slug itself when the project declared none. It is for display only — the slug is what is written to the API — and it never fabricates a label, which is why the fallback is the slug verbatim rather than a title-cased guess.
func (BlockTypeSchema) Doc ¶ added in v0.2.0
func (s BlockTypeSchema) Doc() BlockDoc
Doc projects a block's schema into the compact form printed beside a slug list.
func (BlockTypeSchema) RequiredFields ¶ added in v0.2.0
func (s BlockTypeSchema) RequiredFields() []string
RequiredFields returns the names of the fields proved required.
type Capabilities ¶
type Capabilities struct {
GraphQL GraphQLCapability `json:"graphql"`
Localization Localization `json:"localization"`
Reorder *bool `json:"reorder"`
OG *bool `json:"og"`
MethodOverride *bool `json:"method_override"`
Jobs Jobs `json:"jobs"`
Preferences Preferences `json:"preferences"`
CustomEndpoints []string `json:"custom_endpoints"`
}
Capabilities are the project-level facts.
type Collection ¶
type Collection struct {
Slug string `json:"slug"`
Labels Labels `json:"labels"`
GraphQL *GraphQLNames `json:"graphql"`
IDType string `json:"id_type"`
IDTypeSource string `json:"id_type_source"`
Reachability string `json:"reachability"`
Internal bool `json:"internal"`
Publishable bool `json:"publishable"`
PublishableReason *string `json:"publishable_reason"`
Flags Flags `json:"flags"`
FlagsSource FlagsSource `json:"flags_source"`
Permissions Permissions `json:"permissions"`
Stats Stats `json:"stats"`
FieldsCount int `json:"fields_count"`
FieldsSHA256 string `json:"fields_sha256"`
FieldsShard string `json:"fields_shard"`
FieldsSource string `json:"fields_source"`
TitleField *string `json:"title_field"`
DateFields []string `json:"date_fields"`
Warnings []string `json:"warnings"`
}
Collection is one index entry.
type Diagnostics ¶
type Diagnostics struct {
Requests int `json:"requests"`
ElapsedMS int64 `json:"elapsed_ms"`
Bytes int64 `json:"bytes"`
GraphQLBatches int `json:"graphql_batches"`
Degraded []string `json:"degraded"`
Warnings []string `json:"warnings"`
CreatedAndDeleted []string `json:"created_and_deleted"`
AuthCandidates []string `json:"auth_candidates"`
BlockSlugFiles []string `json:"block_slug_files"`
}
Diagnostics is the cost and degradation record for one discovery run.
type Discoverer ¶
type Discoverer struct {
// contains filtered or unexported fields
}
Discoverer runs §7's pipeline.
func New ¶
func New(opt Options) (*Discoverer, error)
New validates the options and returns a Discoverer.
func (*Discoverer) Run ¶
func (d *Discoverer) Run(ctx context.Context) (*Result, error)
Run executes §7's pipeline and returns the manifest plus one field shard per entity.
The one hard failure is §7.6's: discovery_failed (exit 10) is reserved for the case where REST-only discovery also fails, i.e. GET {api_path}/access itself did not return parseable JSON. A working project must never be made unusable by a GraphQL-layer refusal.
type Field ¶
type Field struct {
Name string `json:"name"`
Path string `json:"path"`
GraphQLPath string `json:"graphql_path"`
Parent *string `json:"parent"`
PayloadType string `json:"payload_type"`
PayloadTypeConfidence string `json:"payload_type_confidence"`
GraphQLType *string `json:"graphql_type"`
JSONType string `json:"json_type"`
Required *bool `json:"required"`
RequiredSource string `json:"required_source"`
HasMany bool `json:"has_many"`
Localized *bool `json:"localized"`
LocalizedSource string `json:"localized_source"`
ReadOnly bool `json:"read_only"`
Options []string `json:"options"`
OptionsSource string `json:"options_source"`
RelationTo []string `json:"relation_to"`
RelationToSource string `json:"relation_to_source"`
Polymorphic bool `json:"polymorphic"`
// WriteShape is mandatory and non-null on every relationship, upload and
// polymorphic field and null on every other kind. §9.10's --set coercion
// reads exactly this key.
WriteShape *string `json:"write_shape"`
Queryable bool `json:"queryable"`
Operators []string `json:"operators"`
Sortable bool `json:"sortable"`
SortableConfidence string `json:"sortable_confidence"`
HookMutated string `json:"hook_mutated"`
Label string `json:"label"`
}
Field is one entry of a field shard.
**Every key below is mandatory on every entry**, using null (or the documented sentinels "n/a" / "unknown") where it does not apply. An agent parses a field entry without presence checks, so `jq '.fields[] | select(.required)'` is total. A unit test asserts the exact key set.
type FieldDoc ¶ added in v0.2.0
type FieldDoc struct {
Description string `json:"description"`
Key string `json:"key"`
// Source is "project-source"; it exists so that a single entry is
// self-describing when it is lifted out of the map.
Source string `json:"source"`
}
FieldDoc is one field's human documentation as the project wrote it.
Key is always reported with Description: `admin.description` is Payload's own field-level key, but PayCLI also accepts a `custom.*` neighbour, and an agent is entitled to know which one a sentence came from.
type Fingerprint ¶
type Fingerprint struct {
TopologySHA256 string `json:"topology_sha256"`
SchemaSHA256 string `json:"schema_sha256"`
CheckedAt time.Time `json:"checked_at"`
ServerIdentity string `json:"server_identity"`
}
Fingerprint carries §8.4's two invalidation levels. Level 1 is identity-scoped and cheap; Level 2 is identity-independent and is the only permission-free schema-version proxy the API offers.
type Flags ¶
type Flags struct {
Upload *bool `json:"upload"`
Auth *bool `json:"auth"`
UseAPIKey *bool `json:"use_api_key"`
Versions *bool `json:"versions"`
Drafts *bool `json:"drafts"`
Trash *bool `json:"trash"`
Folders *bool `json:"folders"`
Duplicate *bool `json:"duplicate"`
Orderable *bool `json:"orderable"`
EndpointsDisabled *bool `json:"endpoints_disabled"`
}
Flags are §7.6's tri-state capability flags. nil means "never learned" and must never block an operation locally — the operation is attempted and the server's answer is classified after the fact.
type FlagsSource ¶
type FlagsSource struct {
Upload string `json:"upload"`
Auth string `json:"auth"`
UseAPIKey string `json:"use_api_key"`
Versions string `json:"versions"`
Drafts string `json:"drafts"`
Trash string `json:"trash"`
Folders string `json:"folders"`
Duplicate string `json:"duplicate"`
Orderable string `json:"orderable"`
EndpointsDisabled string `json:"endpoints_disabled"`
}
FlagsSource carries one provenance string per flag.
type Global ¶
type Global struct {
Slug string `json:"slug"`
Labels Labels `json:"labels"`
GraphQL *GraphQLNames `json:"graphql"`
Reachability string `json:"reachability"`
Internal bool `json:"internal"`
Flags GlobalFlags `json:"flags"`
FlagsSource GlobalFlagsSource `json:"flags_source"`
Permissions GlobalPermissions `json:"permissions"`
FieldsCount int `json:"fields_count"`
FieldsSHA256 string `json:"fields_sha256"`
FieldsShard string `json:"fields_shard"`
FieldsSource string `json:"fields_source"`
Warnings []string `json:"warnings"`
}
Global is one global index entry. A global has no id, no bulk verbs and no trash, so the collection keys that cannot apply are absent rather than written as a misleading false.
type GlobalFlags ¶
GlobalFlags is the reduced flag set a global can have.
type GlobalFlagsSource ¶
GlobalFlagsSource carries provenance for GlobalFlags.
type GlobalPermissions ¶
type GlobalPermissions struct {
Read bool `json:"read"`
Update bool `json:"update"`
ReadVersions bool `json:"read_versions"`
FieldLevel *bool `json:"field_level"`
}
GlobalPermissions is the /api/access globals entry.
type GraphQLCapability ¶
type GraphQLCapability struct {
Mode string `json:"mode"`
Introspection *bool `json:"introspection"`
Path string `json:"path"`
Detail string `json:"detail,omitempty"`
Hint string `json:"hint,omitempty"`
}
GraphQLCapability is §7.6's ladder result. Introspection is tri-state: on a route_missing or unreachable endpoint PayCLI learned nothing about it.
type GraphQLNames ¶
type GraphQLNames struct {
Singular string `json:"singular"`
Plural string `json:"plural,omitempty"`
Count string `json:"count,omitempty"`
Source string `json:"source"`
}
GraphQLNames is the slug <-> GraphQL type mapping recovered by §7.3's index-for-index zip of the Access type against Query.docAccess*. It is nil when no GraphQL name could be established, and the entity then runs REST-only.
type IDLadderInput ¶
type IDLadderInput struct {
// GraphQL is the Stage-1 answer, "" when GraphQL was unavailable.
GraphQL string
// SampleID is docs[0].id from GET /{slug}?limit=1&depth=0&select[id]=true.
SampleID any
// SampleIDPresent distinguishes "the collection is empty" from "id was
// null".
SampleIDPresent bool
// VersionParentID is docs[0].parent from /{slug}/versions, which is the
// *document* id while the version's own id is a different row.
VersionParentID any
VersionParentPresent bool
// Configured is the profile's id_type pin.
Configured string
}
IDLadderInput is §7.6's REST id_type ladder, expressed as already-fetched samples so the ladder itself is pure and table-testable.
type Identity ¶
type Identity struct {
AuthMode string `json:"auth_mode"`
AuthCollection *string `json:"auth_collection"`
AuthCollectionSource string `json:"auth_collection_source"`
UserID any `json:"user_id"`
Verified bool `json:"verified"`
CanAccessAdmin bool `json:"can_access_admin"`
Strategy string `json:"strategy"`
KeyFingerprint string `json:"key_fingerprint"`
}
Identity is Stage 0's verified answer to "who am I". The /me response body itself is never written to disk (§7.8.3) — it carries the API key in plaintext on a project with useAPIKey.
type IntroEnumValue ¶
type IntroEnumValue struct {
Name string `json:"name"`
}
IntroEnumValue is one ENUM value.
type IntroField ¶
type IntroField struct {
Name string `json:"name"`
Args []IntroArg `json:"args"`
Type *TypeRef `json:"type"`
}
IntroField is one field of an OBJECT type.
func (*IntroField) Arg ¶
func (f *IntroField) Arg(name string) (IntroArg, bool)
Arg returns a named argument.
func (*IntroField) HasArg ¶
func (f *IntroField) HasArg(name string) bool
HasArg reports whether a named argument exists.
type IntroInputField ¶
IntroInputField is one field of an INPUT_OBJECT type.
type IntroType ¶
type IntroType struct {
Kind string `json:"kind"`
Name string `json:"name"`
Fields []IntroField `json:"fields"`
InputFields []IntroInputField `json:"inputFields"`
EnumValues []IntroEnumValue `json:"enumValues"`
PossibleTypes []IntroTypeName `json:"possibleTypes"`
}
IntroType is one __type(name:) result.
func (*IntroType) FieldNames ¶
FieldNames returns the OBJECT field names.
func (*IntroType) InputFieldNames ¶
InputFieldNames returns the INPUT_OBJECT field names.
func (*IntroType) PossibleTypeNames ¶
PossibleTypeNames returns a UNION's member names, preserving schema order — the *_RelationTo enum and its matching union list their members in the same order, and the polymorphic target mapping depends on that.
type IntroTypeName ¶
IntroTypeName is a bare type reference in possibleTypes.
type Jobs ¶
type Jobs struct {
Collection *string `json:"collection"`
StatsGlobal *string `json:"stats_global"`
}
Jobs and Preferences name Payload's internal collections when this project has them, so a command never has to hardcode the slug.
type Kind ¶
type Kind struct {
PayloadType string
Confidence string
JSONType string
HasMany bool
ReadOnly bool
Polymorphic bool
Options []string
OptionsSource string
RelationTo []string
RelationToSource string
// Blocks is the UNION member list for a blocks field. These are
// interfaceNames, NOT blockType slugs — §7.10 resolves the real slugs from
// project source.
BlockInterfaces []string
// Children is the named type whose fields should be walked as nested
// group/array entries, or "" when there are none.
Children string
// Leaves are type names this inference needs resolved to be complete.
Leaves []string
}
Kind is the result of §7.4's field-kind inference table.
func InferKind ¶
InferKind implements §7.4's table. It is pure: everything it needs is either in the TypeRef or already in the Schema, and anything missing is reported through Leaves so the caller can batch one more __type round.
ownerSingular is the entity's GraphQL type name, used only to recognise the {Owner}_{field}* naming convention.
type Labels ¶
type Labels struct {
Singular string `json:"singular"`
Plural string `json:"plural"`
Source string `json:"source"`
}
Labels are §7.5 probe 8's harvest. They are never blank, never null and never half-parsed: a failed parse falls back to the title-cased slug with source "derived".
func DeriveLabels ¶
DeriveLabels is the fallback: the title-cased slug with - and _ turned into spaces (crm-contacts -> "Crm Contacts"). Labels are never blank, never null and never half-parsed.
singularType is the entity's GraphQL type name when one is known (CrmContact); it yields a genuinely singular label without any pluralisation guessing, which §7.3 forbids. Without it the plural form is reused, because inventing a singular by stripping an "s" mangles real slugs (`cms` -> `Cm`).
func LabelsFromMessage ¶
LabelsFromMessage builds the labels for a successful probe-8 parse. The singular still comes from the GraphQL type name or the slug, because the message only ever carries the plural.
type LearnedFailure ¶
type LearnedFailure struct {
Operator string `json:"operator"`
Collection string `json:"collection"`
Evidence string `json:"evidence"`
LearnedAt time.Time `json:"learned_at"`
}
LearnedFailure is §7.11's reactive record: an operator that provably failed on a collection, so the next use warns before sending.
type Limitation ¶
type Limitation struct {
Code string `json:"code"`
Entity string `json:"entity,omitempty"`
Field string `json:"field,omitempty"`
Detail string `json:"detail,omitempty"`
Mitigation string `json:"mitigation"`
}
Limitation is a declared hole. Holes are declared, never guessed.
type LocaleAnalysis ¶
type LocaleAnalysis struct {
// Candidates are the true locale codes, or nil when the sample proved
// nothing. These are real codes, unlike the formatName()-mangled names the
// GraphQL LocaleInputType enum exposes.
Candidates []string
// Localized are the field names that came back as a locale map.
Localized []string
// NotLocalized are the field names that did not, on a document where at
// least one sibling did — which is the only evidence REST offers for a
// per-field false.
NotLocalized []string
}
LocaleAnalysis is the result of reading a `?locale=all` sample.
func AnalyzeLocaleAll ¶
func AnalyzeLocaleAll(docs []map[string]any) LocaleAnalysis
AnalyzeLocaleAll implements §7.9b step 2. A localized field comes back as {"en": …, "de": …} and the keys are true codes. The candidate set is the intersection of the key sets that appeared in at least two fields across the sampled documents — one field alone could be an ordinary group whose keys happen to look like codes.
type LocaleInput ¶
type LocaleInput struct {
// Configured is the profile's locales pin.
Configured []string
// Analysis is the locale=all sample result.
Analysis LocaleAnalysis
// EnumNames are LocaleInputType's enumValues, which are
// formatName()-mangled and are therefore NEVER usable as codes.
EnumNames []string
// GraphQLReachable reports whether the cross-validation could run at all.
GraphQLReachable bool
}
LocaleInput is §7.9b's ladder input, already fetched.
type Localization ¶
type Localization struct {
Enabled *bool `json:"enabled"`
// EnabledSource is the story behind `enabled`, which §7.8.3 requires of
// every derived fact: "graphql-arg" / "graphql-arg-absent" when the plural
// Query field settled it, "locale-all" when a ?locale=all sample proved it
// positively, "configured" when the profile pinned locale codes, and
// "unknown" while it is still nil.
EnabledSource string `json:"enabled_source"`
Locales []string `json:"locales"`
// Default is Payload's localization.defaultLocale. No API exposes it, so
// it is null unless something actually established it — it is NEVER the
// first entry of a sorted locale list (§21.3 rule 1: a defaulted value is
// not a discovered fact).
Default *string `json:"default"`
DefaultSource string `json:"default_source"`
LocalesSource string `json:"locales_source"`
FallbackDefault *string `json:"fallback_default"`
LocaleCount *int `json:"locale_count"`
}
Localization models §7.9. `enabled` is tri-state because REST cannot tell a non-localised project from a localised one — `locale` is accepted and ignored when localisation is off.
type Manifest ¶
type Manifest struct {
ManifestVersion int `json:"manifest_version"`
Generation string `json:"generation"`
CLIVersion string `json:"cli_version"`
GeneratedAt time.Time `json:"generated_at"`
ExpiresAt time.Time `json:"expires_at"`
Meta ManifestMeta `json:"meta"`
Fingerprint Fingerprint `json:"fingerprint"`
Source Source `json:"source"`
Identity Identity `json:"identity"`
Capabilities Capabilities `json:"capabilities"`
Collections []*Collection `json:"collections"`
Globals []*Global `json:"globals"`
Unreachable []Unreachable `json:"unreachable"`
Limitations []Limitation `json:"limitations"`
UnsupportedOperators []UnsupportedOperator `json:"unsupported_operators"`
LearnedOperatorFailures []LearnedFailure `json:"learned_operator_failures"`
Diagnostics Diagnostics `json:"diagnostics"`
}
Manifest is the discovery index — everything help text, `pay collections`, `pay explain --slim`, shell completion and target resolution need. It stays roughly 1 KB per collection regardless of field count, because the fields themselves live in per-entity Shards.
func DecodeManifest ¶
DecodeManifest parses a manifest index. A manifest_version other than the current one is reported as a miss by returning ok == false rather than an error, because §8.3 requires every cache read failure to be a miss.
func NewManifest ¶
func NewManifest() *Manifest
NewManifest returns a manifest with every slice non-nil, so the JSON form never has a null where an agent expects an array.
func (*Manifest) AddLimitation ¶
AddLimitation appends a limitation with its §7.6 mitigation, de-duplicating on (code, entity, field) so a per-collection hole is recorded once.
func (*Manifest) AddUnreachable ¶
AddUnreachable appends an unreachable entry, de-duplicating on slug+kind.
func (*Manifest) Collection ¶
func (m *Manifest) Collection(slug string) (*Collection, bool)
Collection returns the index entry for a slug.
func (*Manifest) CollectionSlugs ¶
CollectionSlugs returns every collection slug in index order (key-sorted).
func (*Manifest) GlobalSlugs ¶
GlobalSlugs returns every global slug in index order.
func (*Manifest) LearnedOperatorFailure ¶
func (m *Manifest) LearnedOperatorFailure(operator, collection string) (LearnedFailure, bool)
LearnedOperatorFailure looks the memo up for one operator on one collection. It is deliberately NOT project-wide: `all` failing on crm-contacts says nothing about `all` on pages, and warning about the second because of the first would be the same "assume, do not learn" mistake §1 conflict 36 exists to correct.
func (*Manifest) RecordOperatorFailure ¶
RecordOperatorFailure appends §7.11's reactive memo, or refreshes the record that is already there. It reports whether the manifest changed, so a caller can skip an expensive cache rewrite when it did not.
Records are keyed by (operator, collection): re-running the same failing query updates the evidence and the timestamp instead of growing the file without bound.
func (*Manifest) ResolveCollection ¶
func (m *Manifest) ResolveCollection(slug string) (*Collection, error)
ResolveCollection looks a slug up and produces §11's collection_unknown with did-you-mean candidates when it is absent.
func (*Manifest) ResolveGlobal ¶
ResolveGlobal is ResolveCollection for globals.
func (*Manifest) Revision ¶
Revision is a short, stable identifier for the manifest's content, used as the discovery revision stamped into generated skill docs (§14) and into meta.discovery_revision.
func (*Manifest) Sort ¶
func (m *Manifest) Sort()
Sort orders the index deterministically: collections and globals by slug, and every declared hole by (code, entity, field). Two discovery runs against an unchanged server must produce byte-identical manifests apart from the timestamps, or the §8.4 fingerprints would flap.
type ManifestMeta ¶
type ManifestMeta struct {
Scope string `json:"scope"`
Profiles []string `json:"profiles"`
BaseURL string `json:"base_url"`
APIPath string `json:"api_path"`
GraphQLPath string `json:"graphql_path"`
HeaderNames []string `json:"header_names"`
KeyFingerprint string `json:"key_fingerprint"`
CreatedAt time.Time `json:"created_at"`
ConfirmedAt time.Time `json:"confirmed_at"`
}
ManifestMeta binds the manifest to the cache scope that produced it (§8.1). It carries the sorted header *names* only — a [profiles.X.headers] value is never written to any artefact (§7.8.3).
type Options ¶
type Options struct {
Client *payload.Client
BaseURL string
APIPath string
APIPathSource string
// GraphQLRoute is routes.graphQL, defaulting to /graphql. graphql_path is
// DERIVED from api_path + this (§1 conflict 34), never an independent
// literal.
GraphQLRoute string
GraphQLPath string
GraphQLPathSource string
AuthMode string
AuthCollection string
// AuthCollectionSource is "configured" when the slug was pinned.
AuthCollectionSource string
// CachedAuthCollection is a slug already resolved for this
// (normURL, keyFP) pair by a previous run (§7.0e). When set, Stage -1 is
// skipped entirely.
CachedAuthCollection string
// CredentialAbsent reports anonymous operation. Stage 0's /me request is
// then not made at all and discovery continues.
CredentialAbsent bool
KeyFingerprint string
Concurrency int
Profile string
Profiles []string
ScopeKey string
HeaderNames []string
CLIVersion string
Generation string
Now func() time.Time
TTL time.Duration
// Deep widens Stage 3 to every collection and adds the document census.
Deep bool
// AllowWriteProbes enables §7.7's empty-POST harvest. It creates a real
// document in any collection with no required fields, so it is off by
// default and the flag name is deliberately unpleasant.
AllowWriteProbes bool
// NoLabels skips §7.5 probe 8, which is the only DELETE verb discovery
// ever issues.
NoLabels bool
// NoProbes disables Stage 3 entirely.
NoProbes bool
ConfiguredIDType string
ConfiguredLocales []string
ConfiguredBlocks map[string][]string
CustomEndpoints []string
PayloadVersion string
PayloadVersionSource string
DBAdapter string
DBAdapterSource string
// ProjectBlockSlugs are §7.10's `slug:` literals found on the local
// filesystem, and ProjectBlockSlugFiles the absolute files they came from.
ProjectBlockSlugs []string
ProjectBlockSlugFiles []string
// ProjectBlockInterfaces maps an `interfaceName:` literal found on the
// local filesystem to the `slug:` declared beside it in the same object
// (CallToActionBlock -> cta). It is the other half of §7.10: a blocks
// field's GraphQL union publishes interfaceNames, and only this map turns
// one into a blockType the REST API will accept. Empty is survivable —
// every union member then resolves through the interfaceName heuristic and
// is labelled as inferred — but it is never guessed at silently.
ProjectBlockInterfaces map[string]string
// ProjectBlockDecls are the block declarations read off disk, keyed by
// slug: each block's own `fields:` entries and whether the whole array was
// parseable. This is the ONLY source of a block field's required-ness —
// Payload generates no input type for a block type, so §7.4's NON_NULL
// trick cannot reach inside one. Absent for every plugin-provided block,
// which is why the answer stays tri-state.
ProjectBlockDecls map[string]BlockSourceDecl
// ProjectFieldDocs are the per-field `admin: { description: '…' }`
// instructions read from the project's own collection and global configs,
// keyed by entity slug and then by field name, with ProjectFieldDocFiles
// naming the file each entity's came from.
//
// Payload publishes admin.description nowhere in the API — it is admin-UI
// metadata, absent from both REST responses and GraphQL introspection — so
// the file on disk is the only source there is, and an entity whose config
// PayCLI cannot see (a plugin's collection lives in node_modules) has none.
ProjectFieldDocs map[string]map[string]FieldDoc
ProjectFieldDocFiles map[string]string
// ProjectAuthSlugs are auth-collection slug literals from the project's
// own payload.config.ts, used only to widen Stage -1's candidate set.
ProjectAuthSlugs []string
// ProjectRoutes are `routes:` literals, named in the endpoint_not_payload
// hint when nothing accepted.
ProjectRoutes []string
Logger *slog.Logger
}
Options configures one discovery run.
Everything here is a plain value rather than a config.* type on purpose: internal/discovery must not depend on the configuration layer's shape, and every project fact it needs (block slugs, the payload version, the db adapter) is produced by a local filesystem scan the caller already ran.
type Permissions ¶
type Permissions struct {
Create bool `json:"create"`
Read bool `json:"read"`
Update bool `json:"update"`
Delete bool `json:"delete"`
ReadVersions bool `json:"read_versions"`
Unlock bool `json:"unlock"`
FieldLevel *bool `json:"field_level"`
}
Permissions is the /api/access entry for this identity. field_level is tri-state: `fields` is the boolean true for a privileged key and an object for a restricted one, and it is NEVER used as a field-name source (§1 conflict 12).
type Preferences ¶
type Preferences struct {
Collection *string `json:"collection"`
}
Preferences names the preferences collection when present.
type Result ¶
type Result struct {
Manifest *Manifest
// Shards are keyed by the cache-relative shard path, e.g.
// "fields/pages.json", so a caller can hand them straight to cache.Set.
Shards map[string]*Shard
AuthCollection string
AuthCollectionSource string
// AuthResolved reports that Stage -1 ran and produced a slug worth
// persisting to auth-resolution.json.
AuthResolved bool
APIPath string
APIPathSource string
GraphQLPath string
GraphQLPathSource string
Warnings []output.Warning
}
Result is one discovery run's output. Nothing here has been written to disk: persistence is internal/cache's job, and keeping it there means a failed write is a warning rather than a corrupt manifest.
type Schema ¶
type Schema struct {
// Types are the resolved __type results, keyed by type name.
Types map[string]*IntroType
// SlugBySingular maps a GraphQL singular type name back to its slug, so a
// monomorphic relationship target is reported as a slug, never as a type
// name an agent cannot pass on the command line.
SlugBySingular map[string]string
}
Schema is everything the field-kind inference needs to resolve a named type without another round trip.
type Shard ¶
type Shard struct {
Generation string `json:"generation"`
Slug string `json:"slug"`
SHA256 string `json:"sha256"`
Fields []Field `json:"fields"`
// JoinFields lists read-only join fields, which must be requested with
// joins[field][limit] rather than selected like a normal field.
JoinFields []string `json:"join_fields"`
// Blocks maps a blocks field's path to its resolved blockType slugs. It is
// null — not an empty map — when no source could resolve them, which is
// the difference between "this project has no blocks" and "PayCLI does not
// know" (§7.10). Every entry is resolved from THAT FIELD's own GraphQL
// union, so two blocks fields on the same entity can and do carry
// different lists.
Blocks map[string][]string `json:"blocks"`
// BlocksSource is the entity-wide summary: the one source when every
// blocks field resolved the same way, "mixed" when they did not, "unknown"
// when the entity has no resolved blocks field at all. It is a SUMMARY —
// BlockFields[path].Source is the per-field answer and the one an agent
// should read.
BlocksSource string `json:"blocks_source"`
// BlockFields carries the full per-field answer for every blocks field,
// including each slug's own provenance and the GraphQL union the slugs
// were derived from. It is null when the entity has no blocks field.
BlockFields map[string]BlockField `json:"block_fields"`
// BlockSchemas is every reachable block type's OWN field schema, keyed by
// blockType slug — what is inside a cta, not merely that cta exists. It is
// null on a shard written before the key existed and on a REST-only shard
// (there is no union to read), which is why every consumer treats null as
// "not discovered" and says so rather than as "this block has no fields".
BlockSchemas map[string]BlockTypeSchema `json:"block_schemas"`
// RequiredPaths is the flattened list of paths whose required is true.
RequiredPaths []string `json:"required_paths"`
// FieldDocs are the per-field human instructions this entity's own config
// declares — `admin: { description: '…' }` — keyed by field path.
//
// It is a SEPARATE map rather than three more keys on every field entry,
// and that is a size decision with a measured reason: `pay describe pages`
// carries ~90 field entries, so three keys each would add ~16 KB to the
// most frequently run command in order to publish null 90 times. Here the
// cost is proportional to what the project actually documented, and is
// nothing at all on a project that documented nothing.
//
// FieldDocsSource is what distinguishes "scanned, found none" from "never
// scanned": a producer records no map rather than an empty one.
FieldDocs map[string]FieldDoc `json:"field_docs,omitempty"`
// FieldDocsSource is "project-source" when this entity's config was read
// off disk and "unknown" when it was not — the normal case for a
// plugin-provided collection, whose config lives in node_modules.
FieldDocsSource string `json:"field_docs_source,omitempty"`
// FieldDocsFile is the absolute path the docs were read from, "" when
// there is none.
FieldDocsFile string `json:"field_docs_file,omitempty"`
}
Shard is one entity's field schema — the artefact decoded only for the collection named on the command line (§8.2).
func (*Shard) BlockFieldFor ¶ added in v0.2.0
func (s *Shard) BlockFieldFor(path string) (BlockField, bool)
BlockFieldFor returns the full per-field record for a blocks field.
func (*Shard) BlockSchemaFor ¶ added in v0.2.0
func (s *Shard) BlockSchemaFor(slug string) (BlockTypeSchema, bool)
BlockSchemaFor returns one blockType's field schema and whether the shard carries it. The bool is the tri-state: false is "PayCLI did not discover this block's interior", never "the block has no fields".
func (*Shard) BlockSlugsFor ¶ added in v0.2.0
BlockSlugsFor returns every blockType slug reachable from any blocks field of this entity, sorted and deduplicated.
func (*Shard) BlockTypesFor ¶ added in v0.2.0
BlockTypesFor returns the blockType slugs one field accepts, and whether PayCLI knows. The bool is the §7.10 tri-state: false means unknown, not "accepts nothing".
func (*Shard) DateFields ¶
DateFields returns every field whose payload_type is date, in shard order.
func (*Shard) DocumentedPaths ¶ added in v0.2.0
DocumentedPaths lists every field path carrying a description, sorted.
func (*Shard) FieldDocFor ¶ added in v0.2.0
FieldDocFor returns one field path's documentation and whether the project declared any. The bool is the tri-state: false is "nobody wrote one", never an empty sentence.
func (*Shard) Finalize ¶
func (s *Shard) Finalize()
Finalize sorts the derived lists, recomputes required_paths and stamps the content hash. It is the single place a shard becomes publishable, so a caller cannot forget the hash the index refers to.
func (*Shard) QueryablePaths ¶
QueryablePaths returns the subset that Payload's where parser accepts. It feeds query.Schema, whose nil-means-unknown contract this preserves: an empty shard yields nil, never an empty non-nil slice that would reject everything.
func (*Shard) SetBlockField ¶ added in v0.2.0
func (s *Shard) SetBlockField(bf BlockField)
SetBlockField records ONE blocks field's resolution and keeps every derived view of it consistent: block_fields[path] (the full answer), blocks[path] (the slug list §9.7 validates a --set blockType against) and blocks_source (the entity-wide summary).
It is the only writer of those three keys, so they cannot drift apart and a producer cannot record slugs without also recording where they came from (§7.8.3).
func (*Shard) SetFieldDocs ¶ added in v0.2.0
SetFieldDocs records this entity's per-field documentation and the file it was read from, keeping the three keys that describe it consistent.
It is the only writer of them, so a producer cannot record documentation without also recording where it came from (§7.8.3). Calling it with an empty map still records the SOURCE: "the config was read and documents nothing" is a different, and more useful, answer than "no config was read".
func (*Shard) SortablePaths ¶
SortablePaths returns the sortable subset, with the same nil contract.
type Source ¶
type Source struct {
BaseURL string `json:"base_url"`
APIPath string `json:"api_path"`
APIPathSource string `json:"api_path_source"`
GraphQLPath string `json:"graphql_path"`
GraphQLPathSource string `json:"graphql_path_source"`
PoweredBy string `json:"powered_by"`
PayloadVersion *string `json:"payload_version"`
PayloadVersionSource string `json:"payload_version_source"`
DBAdapter string `json:"db_adapter"`
DBAdapterSource string `json:"db_adapter_source"`
}
Source records how PayCLI reached the server and what it could learn about the project itself. payload_version and db_adapter are not obtainable from the API at all (§7.11), so both are nullable with provenance.
type Stats ¶
Stats is the optional document census. total_docs is nil unless the caller asked for it (--deep): one count request per collection is 49 requests on the live project and §7.1's cold budget is four.
type TopologyProjection ¶
type TopologyProjection struct {
Collections map[string][]string `json:"c"`
Globals map[string][]string `json:"g"`
Admin bool `json:"admin"`
}
TopologyProjection is §8.4's Level-1 normalised projection of GET /api/access. Only key *sets* are hashed, never values, so a permission flip still changes the fingerprint (a collapsed entry has a different key set) while transient data does not.
func ProjectTopology ¶
func ProjectTopology(collections, globals map[string]any, canAccessAdmin bool) TopologyProjection
ProjectTopology builds the projection from a decoded /api/access body.
type TypeRef ¶
type TypeRef struct {
Kind string `json:"kind"`
Name string `json:"name"`
OfType *TypeRef `json:"ofType"`
}
TypeRef is a GraphQL type reference as introspection returns it: a chain of NON_NULL / LIST wrappers around one named type.
type Unreachable ¶
type Unreachable struct {
Slug string `json:"slug"`
Kind string `json:"kind"`
Reason string `json:"reason"`
Detail string `json:"detail"`
}
Unreachable records an entity PayCLI knows about but cannot fully use, with the evidence for why.
type UnsupportedOperator ¶
type UnsupportedOperator struct {
Operator string `json:"operator"`
Collection string `json:"collection,omitempty"`
Reason string `json:"reason"`
}
UnsupportedOperator is a pre-emptive client-side block. It is only ever populated when db_adapter_source == "configured" (§7.11): on MongoDB `all` maps to $all and works, so a guessed list would make PayCLI refuse a query the server would have answered.