api

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package api wraps the Hadron GraphQL endpoint: a genqlient client for typed operations, a raw escape hatch for `hadron api`, and the mapping from transport/GraphQL errors to exit codes.

Index

Constants

View Source
const EnvAllowHTTP = urlsec.EnvAllowHTTP

EnvAllowHTTP opts out of the HTTPS-enforcement guard for a trusted local or self-hosted server (set to "1").

View Source
const NodeBatchCap = 200

NodeBatchCap mirrors hadron-server's BATCH_READ_MAX_NODES (cor:api:040): a single nodeBatch call accepts at most 200 refs and fails loud above that, so bulk reads fan out in fixed-size chunks. The server also enforces a ~1 MB response cap that can return a partial page (truncated=true) with the spillover in `omitted`; CollectNodeBatch re-requests those.

View Source
const PageLimit = 200

PageLimit is the server's hard cap on a single page of the uniform paginated list queries (hadron-server#473: limit default 50, cap 200).

Variables

View Source
var ErrRedirectPolicy = errors.New("redirect refused by policy")

ErrRedirectPolicy marks a refusal WE made about a redirect, as opposed to a network failure. net/http wraps a CheckRedirect error in *url.Error, which satisfies net.Error — so without this sentinel the transport classifier (#394) would read our own HTTPS-downgrade refusal as a lost request and report it as retryable exit 7. The server answered; retrying cannot help.

Functions

func CollectAll added in v0.5.1

func CollectAll[T any](fetch func(limit, offset int) ([]T, int, error)) ([]T, error)

CollectAll drains a uniform paginated { items, total } list query (hadron-server#473) to exhaustion, so "whole scope" commands never silently truncate at one server page. fetch receives (limit, offset) and returns one page's items plus the envelope total.

func CollectNodeBatch added in v0.2.0

CollectNodeBatch fetches full nodes for refs in cap-sized chunks, re-queuing the spillover the server drops under its response-size cap. A ref is a node PK or a fully-qualified node URN (hadron-server#813) — `omitted` and `unavailable` come back as the refs were sent, so re-queuing and reporting both stay in the caller's own vocabulary. fetch is injected rather than calling gen.NodeBatch directly so the chunking/truncation loop is unit-testable without a server and so each caller controls the field projection. Returns the nodes (input order is not preserved across chunks), the union of refs the server reported unavailable, and the first error.

Shared by the whole-corpus fan-outs (`memory export`, `spec get --prefix`).

func CollectUntil added in v0.5.1

func CollectUntil[T any](fetch func(limit, offset int) ([]T, int, error), done func(items []T) bool) ([]T, error)

CollectUntil is CollectAll with an early exit: after each page, done receives the items accumulated so far, and returning true stops paging (the accumulated items are returned as-is). Use it when a caller can tell mid-scan that it already has what it needs — e.g. an exact match in a sorted list — without draining the remaining pages.

func DescendantCount added in v0.8.0

func DescendantCount(err error) int

DescendantCount returns the descendant count carried by a NODE_HAS_DESCENDANTS error (server #661: its extensions.count), or -1 when err is not that error or carries no non-negative numeric count. JSON numbers decode to float64, but a few other numeric shapes are tolerated. A negative or non-numeric value is treated as "no count" (-1) so callers never render a nonsensical "-N descendant(s)". Call it BEFORE MapError wraps the error.

func DocumentFromBatchNode added in v0.3.0

func DocumentFromBatchNode(n *batchNode) *nodedoc.Document

DocumentFromBatchNode maps a bulk-read node into the neutral nodedoc.Document the markdown/JSON codecs consume. It is the single gen→Document mapping shared by `memory export` and `node export`. Type "" carries the server default (info), so it never serializes and a re-import defaults correctly. The projection only carries the memory id, not the URN, so callers that emit a standalone file resolve and set Document.MemoryURN themselves.

func DocumentHasMutation added in v0.9.0

func DocumentHasMutation(doc string) bool

DocumentHasMutation reports whether a GraphQL document contains a mutation operation, so `hadron api` can warn about idempotency only when a write was actually at stake.

String literals are skipped so a query selecting a field whose *value* mentions mutations is not misread. Beyond that the check is deliberately generous — a false positive prints a warning that is merely unnecessary, while a false negative withholds the one sentence that prevents a double-applied write. It errs toward warning.

func Endpoint

func Endpoint(serverURL string) string

Endpoint joins the server base URL with the GraphQL path.

func HasErrorCode added in v0.5.1

func HasErrorCode(err error, code string) bool

HasErrorCode reports whether err carries a GraphQL error whose extensions.code equals code. It inspects the raw genqlient error (call it BEFORE MapError wraps the error into a CodedError) so callers can branch on a specific server error — e.g. `node import` falling back from updateNode's NODE_NOT_FOUND to createNode.

func MapError

func MapError(err error) error

MapError converts transport and GraphQL errors into CodedErrors so the root command can derive the documented exit code. Codes come from hadron-server's Apollo resolvers (extensions.code).

func NewClient

func NewClient(serverURL, token string, httpClient *http.Client) (graphql.Client, error)

NewClient returns a genqlient client for the given server, authenticating with token (may be empty for anonymous calls).

func RequireSecureURL added in v0.6.0

func RequireSecureURL(serverURL, token string) error

RequireSecureURL refuses to transmit the bearer token over a non-https server URL — cleartext credentials are trivially captured by an on-path attacker on the shared CI/dev machines this CLI runs on (#114). Carve-outs: a loopback host (local dev, incl. the test httptest servers) and HADRON_ALLOW_HTTP=1 (a trusted self-hosted backend). An empty token means no credential rides, so the check is a no-op — anonymous http is allowed.

Types

type FindNodesPage added in v0.5.0

type FindNodesPage struct {
	Nodes    []*ListNode
	Total    *int
	Degraded *string
	Reason   *string
}

FindNodesPage is the flattened result of one findNodes call: the hit nodes (hits[].node hoisted to a bare slice, the shape every old `nodes`/`nodeSearch` caller expects), plus the envelope's total and the degraded/reason notes that `spec find` surfaces on a vector-less memory. Nodes is always non-nil.

func FindNodes added in v0.5.0

func FindNodes(
	ctx context.Context,
	client graphql.Client,
	query *string,
	mode *gen.FindNodesMode,
	filter *gen.NodeFilter,
	sort *gen.NodeSort,
	sortProperty *gqltypes.NodePropertySort,
	limit, offset *int,
) (*FindNodesPage, error)

FindNodes runs the unified node search/list (cor:api:090) and flattens the hits[].node envelope into a bare node slice. Omit query for a filtered list in deterministic order (the old `nodes` semantics); pass query + mode to rank (the old `nodeSearch`). sortProperty orders by a properties/data JSON path and overrides sort when set (#719). All args are optional; nil pointers are omitted from the wire so the server applies no constraint. The caller maps GraphQL errors through MapError as usual.

type HeldDetail added in v0.11.0

type HeldDetail struct {
	WorkerID   string
	HolderID   string
	HolderName string
	HeldAt     string
}

HeldDetail is the payload a WORKER_HELD error carries (hadron-server#1050): whose name it is, and since when.

TWO server paths raise this code and they do NOT carry the same fields. The pre-transaction check resolves the holder and sends workerId, heldBy, heldByName and heldAt; the compare-and-set inside the session-creating transaction — the one that refuses the loser of a race — sends only workerId and heldBy, because it has no holder read to spare. So HolderName and HeldAt are absent on a perfectly ordinary refusal, and a caller that renders them unconditionally prints a half-empty sentence on the path a concurrent bind takes. Every field is best-effort; only ok is a promise.

func WorkerHeldDetail added in v0.11.0

func WorkerHeldDetail(err error) (HeldDetail, bool)

WorkerHeldDetail extracts the WORKER_HELD payload from err's extensions; ok is false when err is not that error. Rendered from the extensions, not the message wording (cor:agt:020:09 is the contract). Call it BEFORE MapError wraps the error.

HELD is not TAKEN and the difference is the whole point: a held name is somebody's until they release it, so this refusal has no --force. Never pair it with a takeover suggestion.

func (HeldDetail) Holder added in v0.11.0

func (d HeldDetail) Holder() string

Holder names the person holding the name, preferring the handle the server resolved and falling back to the raw user id — which is what the race path leaves us with. Empty only when the server sent neither.

type ListNode added in v0.5.0

ListNode is the shallow node projection the unified `findNodes` field returns under hits[].node — id/loc/name/type/tags/seq/isRunnable/updatedAt. Aliased here so callers don't spell the deeply-nested genqlient type name (and so a future projection change is a one-line edit), matching the batchNode alias pattern in nodedoc.go.

type RawResult

type RawResult struct {
	Body   json.RawMessage
	Errors []rawError
}

RawResult is the verbatim GraphQL response envelope.

func RawGraphQL

func RawGraphQL(ctx context.Context, serverURL, token, query string, variables map[string]any, httpClient *http.Client) (*RawResult, error)

RawGraphQL posts an arbitrary query/mutation to the server and returns the raw response body. Used by `hadron api`. The returned error carries the mapped exit code when the response contains GraphQL errors.

func (*RawResult) Err

func (r *RawResult) Err() error

Err returns a CodedError summarizing the response's GraphQL errors, or nil when the response is error-free.

type SearchHit added in v0.6.0

type SearchHit struct {
	Score         *float64
	AbstractStale bool
	Node          *SearchNode
}

SearchHit is one scored search result. Score is nil when the server returned an unscored hit; AbstractStale reports the vector index's abstract-staleness flag (false when absent).

type SearchNode added in v0.6.0

SearchNode is the search-shaped node projection (abstract included).

type SearchPage added in v0.6.0

type SearchPage struct {
	Hits     []*SearchHit
	Total    *int
	Degraded *string
	Reason   *string
}

SearchPage is the scored counterpart of FindNodesPage: it preserves per-hit scores rather than flattening to bare nodes.

func SearchNodes added in v0.6.0

func SearchNodes(
	ctx context.Context,
	client graphql.Client,
	query string,
	mode *gen.FindNodesMode,
	filter *gen.NodeFilter,
	sortProperty *gqltypes.NodePropertySort,
	limit, offset *int,
) (*SearchPage, error)

SearchNodes runs a ranked findNodes query (the `hadron search` backend), keeping per-hit score + vector metadata that FindNodes drops. sortProperty orders by a properties/data JSON path and overrides the mode ranking window when set (#719); nil pointers are omitted from the wire.

type TakenDetail added in v0.9.0

type TakenDetail struct {
	WorkerID   string
	SessionID  string
	LastDriver string
	LastSeenAt string
}

TakenDetail is the informed-takeover payload a WORKER_TAKEN error carries (hadron-server#940): everything the takeover prompt needs, in one round trip. Fields are empty when the server omitted them (lastDriver is null for an unattributed session).

func WorkerTakenDetail added in v0.9.0

func WorkerTakenDetail(err error) (TakenDetail, bool)

WorkerTakenDetail extracts the WORKER_TAKEN payload from err's extensions; ok is false when err is not that error. The MESSAGE also narrates the payload today, but the extensions are the documented contract (cor:agt:020:03) — render from these, not from message wording. Call it BEFORE MapError wraps the error.

Directories

Path Synopsis
Package gqltypes holds hand-authored Go types that the generated genqlient client binds to (see genqlient.yaml `bindings`).
Package gqltypes holds hand-authored Go types that the generated genqlient client binds to (see genqlient.yaml `bindings`).

Jump to

Keyboard shortcuts

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