client

package
v0.28.5 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package client embeds and serves the nexus client SDK — a JS/TS runtime, generated TypeScript types, and Vue 3 composables — directly from the Go binary. No separate npm package; the SDK ships with the app.

Mount installs four routes under cfg.Path (default "/__nexus/client"):

GET  <path>/manifest.json    application/json
GET  <path>/client.js        application/javascript (ESM)
GET  <path>/client.d.ts      application/typescript (generated)
GET  <path>/vue.js           application/javascript (ESM, Vue 3)

The manifest is the foundation. It enumerates every endpoint (REST, GraphQL, WebSocket), their typed args/return schemas, the running auth strategy, and a deduped pool of named struct types. client.js fetches it on construct; client.d.ts is generated from it at boot.

Index

Constants

View Source
const DefaultPath = "/__nexus/client"

DefaultPath is the URL prefix the SDK routes mount under when Config.Path is empty. Sits beside the dashboard at /__nexus so the framework's "introspection + tooling surface" stays under one namespace.

View Source
const SchemaVersion = "client.v1"

SchemaVersion is the contract version emitted in Manifest.Version. SDK consumers gate on the major: a "client.v1" manifest is guaranteed to be readable by any v1-aware consumer. Additive changes (a new EndpointInfo field) leave the version untouched; field removal or shape change bumps the major.

Variables

This section is empty.

Functions

func GenerateDTS

func GenerateDTS(m Manifest) string

GenerateDTS projects a SDK manifest into a TypeScript declaration (.d.ts) file. The output is a single string the embed handler serves verbatim from /__nexus/client/client.d.ts; consumers point their tsconfig at it (or paste it into their repo if they prefer a checked-in artifact via `nexus client --out`).

Output shape:

  1. // header comment carrying the schema version + a "do not edit" banner.
  2. One `export interface <Name>` per entry in manifest.Refs — the deduped pool of named struct types referenced by endpoints.
  3. `export interface RestEndpoints` discriminated map keyed by "<METHOD> <PATH>" → { args, return }. Apps pin call sites by using these keys in TS overloads.
  4. `export interface GraphqlOps` keyed by op name.
  5. `export interface WSMessages` keyed by WS path; each path is a map of msgType → args.
  6. `declare module '/__nexus/client/client.js'` — typed runtime surface (NexusClient, NexusError, CrudHandle, WSHandle, AuthNamespace, token stores).
  7. `declare module '/__nexus/client/vue.js'` — typed Vue 3 composables (useNexus, useQuery, useMutation, useCrud, useWS, useAuth, …) so TS apps importing from the SDK's vue.js URL get completion + signature checks.

Pure function: same manifest in, same string out. Caller (Handler.build) memoizes via sync.Once.

func MergePathsConfig added in v0.28.4

func MergePathsConfig(configPath, outDir string, stdout io.Writer) error

MergePathsConfig writes (or merges into) a jsconfig.json / tsconfig.json at configPath, adding compilerOptions.paths entries that map the runtime URL imports back to the SDK files in outDir. Existing fields (compilerOptions.target, include, exclude, custom paths entries) are preserved entry-for-entry.

File shape is identical for jsconfig and tsconfig — caller picks the filename. compilerOptions.baseUrl defaults to "." when missing (required for paths to resolve).

Exported for the same reason as WriteIfChanged: the CLI flag (--tsconfig / --jsconfig) and the in-process Dump path share the same merge logic.

func RuntimeJS

func RuntimeJS() []byte

RuntimeJS returns the embedded nexus-client.js bytes. Public so the `nexus client --out` CLI can dump the runtime to disk without re-embedding a copy in cmd/nexus. Returns the same byte slice the HTTP handler serves at <path>/client.js.

func VueJS

func VueJS() []byte

VueJS returns the embedded nexus-vue.js bytes. Same role as RuntimeJS for the Vue 3 composables module.

func WriteIfChanged added in v0.28.4

func WriteIfChanged(path string, body []byte, stdout io.Writer) error

WriteIfChanged writes body to path only when the file is missing or its current contents differ from body. Logs "wrote" with the byte count on a real write, "unchanged" when the disk copy already matched. Skipping the no-op write preserves mtime — file watchers (vite, webpack-dev-server, JetBrains' indexer) don't re-trigger on idempotent re-runs.

Bytes-equal comparison rather than hash because the SDK files are tens of KB at most; the explicit byte-slice equality is allocation-free for the common no-change case.

Exported because the CLI (cmd/nexus/client_cmd.go) and the in-process Dump path share the same write contract — keeping one helper means a fix for either site lands everywhere.

Types

type AuthInfo

type AuthInfo struct {
	ExtractorInfo
	LoginPath  string `json:"loginPath,omitempty"`
	LogoutPath string `json:"logoutPath,omitempty"`
	MePath     string `json:"mePath,omitempty"`
}

AuthInfo describes the running auth surface. The extractor shape tells the SDK where to put the access token; LoginPath / LogoutPath / MePath are discovered from endpoints marked via nexus.AuthRoute.

type Config

type Config struct {
	// Enabled gates the entire mount. Default false.
	Enabled bool

	// Path is the URL prefix the SDK routes mount under. Default
	// DefaultPath ("/__nexus/client"). The dashboard at /__nexus/*
	// and the SDK at /__nexus/client/* share an obvious namespace;
	// the SDK routes are PUBLIC (no admin-token gate) because a
	// browser bundle has to fetch them at runtime.
	Path string

	// Middleware applies to every SDK route. Useful when an app
	// gates its dashboard but wants the SDK manifest publicly
	// readable. Empty by default — the SDK is meant to be public.
	Middleware []gin.HandlerFunc

	// Manifest overrides the default per-build manifest projection.
	// Most apps leave this nil; the framework's default reads the
	// live registry. Override for multi-tenant filtering or custom
	// type-stripping. Called once on first request, then cached;
	// see Handler.Reload to invalidate.
	Manifest func() Manifest

	// OutDir, when non-empty, makes Mount also dump the SDK files
	// (client.js, vue.js, manifest.json, client.d.ts) to disk on
	// startup so a frontend's filesystem-based tooling (TypeScript
	// compiler, Vite, JetBrains, VS Code) can resolve types and
	// runtime imports without a manual `nexus client --out` step.
	//
	// Idempotent: files are byte-compared and skipped when content
	// matches, preserving mtime — file-watcher reloads + IDE
	// re-indexing don't fire on no-op restarts.
	//
	// Recommend leaving empty in production builds. Apps that need
	// it gated to dev only can branch on an env var when building
	// the Config (this field intentionally has no built-in env
	// magic — explicit beats implicit for "writes files to your
	// project tree").
	OutDir string

	// TSConfig, when non-empty, makes Mount merge path mappings
	// (the runtime URL imports → OutDir files) into the named
	// jsconfig.json or tsconfig.json on startup. Existing fields
	// (compilerOptions.target, include, exclude, custom paths)
	// survive untouched. Same idempotent contract as OutDir.
	//
	// Only takes effect when OutDir is also set — the path
	// mappings need a target.
	TSConfig string
}

Config tunes the embedded SDK auto-mount. Zero value Enabled=false so omitting Config.Client from nexus.Config is safe — apps that don't ship an SDK don't pay the embed cost.

type EndpointInfo

type EndpointInfo struct {
	Service     string            `json:"service"`
	Module      string            `json:"module,omitempty"`
	Transport   string            `json:"transport"` // "rest" | "graphql" | "websocket"
	Method      string            `json:"method"`
	Path        string            `json:"path"`
	Name        string            `json:"name"`
	Description string            `json:"description,omitempty"`
	Args        *registry.TypeRef `json:"args,omitempty"`
	Return      *registry.TypeRef `json:"return,omitempty"`

	// AuthRequired is true when the endpoint is gated by auth.Required
	// (or any middleware whose name starts with "auth:" — covers
	// Required + Requires).
	AuthRequired bool `json:"authRequired,omitempty"`

	// RequiresPerm is the list of permission strings auth.Requires
	// declared. Empty for unauthed endpoints or auth.Required without
	// specific perms. Derived from middleware names of the form
	// "auth:requires:perm-a,perm-b".
	RequiresPerm []string `json:"requiresPerm,omitempty"`

	// AuthFlow is "login" | "logout" | "me" when the endpoint was
	// marked via nexus.AuthRoute. Empty for normal endpoints.
	AuthFlow string `json:"authFlow,omitempty"`

	Deprecated        bool   `json:"deprecated,omitempty"`
	DeprecationReason string `json:"deprecationReason,omitempty"`
}

EndpointInfo is one callable surface — a REST route, a GraphQL op, or a WebSocket message handler.

type ExtractorInfo

type ExtractorInfo struct {
	Strategy   string          `json:"strategy"`
	HeaderName string          `json:"headerName,omitempty"`
	CookieName string          `json:"cookieName,omitempty"`
	Chain      []ExtractorInfo `json:"chain,omitempty"`
}

ExtractorInfo describes how the running auth module pulls tokens off requests. Mirrors auth.ExtractorInfo (the canonical definition) — duplicated here so the SDK manifest stays auth-package-independent and avoids the nexus → client → auth import cycle. nexus.New is responsible for translating from auth.ExtractorInfo via the AuthInfo callback on client.Config.

Strategy values match auth.ExtractorInfo.Strategy:

"bearer"  HeaderName populated; SDK sends "Authorization: Bearer"
"cookie"  CookieName populated; SDK sets credentials: 'include'
"apikey"  HeaderName populated; SDK sends a bare header value
"chain"   Chain populated with the underlying strategies
"custom"  user-supplied extractor; SDK falls back to credentials

type Handler

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

Handler holds the live state of a mounted SDK surface — the cached manifest + .d.ts strings and a sync.Once that gates the first build. Returned by Mount so tests (and apps that need to invalidate the cache) can call Reload().

func Mount

func Mount(e *gin.Engine, reg *registry.Registry, authInfo func() ExtractorInfo, schemaRefs func() map[string]registry.NamedType, basePath string, cfg Config) *Handler

Mount installs the SDK routes on engine. Idempotent: re-mounting rebuilds the cached manifest by invalidating the Once. Called automatically by nexus.New when Config.Client.Enabled.

authInfo may be nil — apps without auth.Module wired pass nil and the manifest's Auth section is omitted. When non-nil, the callback returns the active extractor strategy + parameters; nexus.New builds it from *auth.Manager.Info() and supplies it here.

schemaRefs is the live pool of named-struct types walked at endpoint registration; the manifest's Refs section reads from it. basePath is the deployment-wide route prefix (app.routePrefix), stamped onto Manifest.BasePath so the SDK prepends it to every call.

When cfg.OutDir is set, Mount also dumps the SDK files to disk after registering routes so frontend tooling (TS compiler, IDE) can resolve types/imports without a manual `nexus client --out` step. Dump errors are logged but don't fail Mount — dev-tool convenience shouldn't crash boot.

func (*Handler) Dump added in v0.28.4

func (h *Handler) Dump(outDir, tsconfig string, stdout io.Writer) error

Dump writes the embedded SDK runtime + the live manifest + generated .d.ts to outDir. Optionally merges path mappings into tsconfig (or jsconfig — same shape) at the given path so an IDE resolves the runtime URL imports back to the dumped files.

Idempotent: WriteIfChanged compares bytes before writing, so re-running against an already-up-to-date target preserves the existing files' mtime — no file-watch / IDE-reindex churn on no-op runs.

outDir is created if missing. tsconfig is optional; pass "" to skip the IDE-config step. Errors short-circuit; partial writes (some files written, then a failure) are possible but rare — the helper writes small files in a fixed order and the filesystem operations themselves rarely fail mid-batch.

stdout receives one line per file ("wrote …" / "unchanged …"). Pass io.Discard to silence.

func (*Handler) Manifest

func (h *Handler) Manifest() Manifest

Manifest returns the projected SDK manifest, building it from the registry on first call and caching the result. Exposed so tests can introspect the manifest without going through the HTTP layer.

func (*Handler) Reload

func (h *Handler) Reload()

Reload drops the cached manifest + .d.ts so the next request rebuilds. Used by tests and by apps that register endpoints via late mount paths. Production callers usually don't need it — the registry stops mutating after fx.Start, so the first build is the only build.

func (*Handler) SetAuthInfo

func (h *Handler) SetAuthInfo(fn func() ExtractorInfo)

SetAuthInfo installs (or replaces) the auth-info provider used to populate the manifest's Auth section on next build. Wired by auth.Module's option chain when both the SDK and auth are in the same app — separates the cycle (client/ doesn't import auth/, auth/ doesn't import client/, the bridge lives in nexus/).

Calls Reload internally so the cached manifest rebuilds with the new auth shape on the next request.

type Manifest

type Manifest struct {
	Version   string                        `json:"version"`
	BasePath  string                        `json:"basePath"`
	Services  []ServiceInfo                 `json:"services,omitempty"`
	Endpoints []EndpointInfo                `json:"endpoints,omitempty"`
	Resources []ResourceInfo                `json:"resources,omitempty"`
	WS        []WSPathInfo                  `json:"ws,omitempty"`
	Auth      *AuthInfo                     `json:"auth,omitempty"`
	Refs      map[string]registry.NamedType `json:"refs,omitempty"`
}

Manifest is the SDK-tailored shape served at GET <path>/manifest.json. Distinct from manifest.Manifest (the deploy-time, admin-token-gated document): public, type-rich, and scoped to what an SDK consumer needs to call into the app.

type ResourceInfo

type ResourceInfo struct {
	Name        string `json:"name"`
	Kind        string `json:"kind"`
	Description string `json:"description,omitempty"`
}

ResourceInfo is the SDK view of a resource — light enough to drive a "where does this go?" picker in a UI without leaking orchestration details.

type ServiceInfo

type ServiceInfo struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
}

ServiceInfo is the SDK-friendly view of a service. Drops topology fields (ResourceDeps, ServiceDeps, Remote) the SDK doesn't need.

type WSMessage

type WSMessage struct {
	Type        string            `json:"type"`
	Args        *registry.TypeRef `json:"args,omitempty"`
	Description string            `json:"description,omitempty"`
}

WSMessage is one (msgType → args) entry on a WS path.

type WSPathInfo

type WSPathInfo struct {
	Path     string      `json:"path"`
	Service  string      `json:"service"`
	Messages []WSMessage `json:"messages"`
}

WSPathInfo groups every typed AsWS message under one WebSocket path so the SDK can build a single connection handle per path with a typed dispatch table.

Jump to

Keyboard shortcuts

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