Skip to main content
Version: 3.0 (next)
Coming soon

App Studio is coming soon: it is in development and testing and is not part of release 3.0. This reference describes the SDK as it is being built and may change before release.

Generated

Generated from the SDK sources by scripts/appstudio-sdk-docs.cjs for runtime-v1 — do not edit; run npm run appstudio:docs:sdk.

@maestrohub/sdk

Runtime, data, live-topic, action and storage APIs available to App Studio apps.

Runtime: runtime-v1. 119 exports (47 values, 72 types). Import with:

import { ... } from '@maestrohub/sdk';

Hooks​

useAlerts​

function useAlerts(limit?: number): AlertsState

useAlerts renders the organization's alarm transitions as they happen — fires and clears — newest first, at most limit (default 200). Pair it with alerts.recent() for the alarms that were already active when the app opened. Needs the uns:read scope.

Example

const a = useAlerts();
return a.error ? <ErrorNotice error={a.error} /> : <Timeline items={a.events.map(e => ({ at: e.firedAt, title: e.topicPath, tone: e.cleared ? 'ok' : 'critical' }))} />;

useAppContext​

function useAppContext(): AppContextState

useAppContext returns user/org/theme (throws outside AppProvider).

useExecutionStream​

function useExecutionStream(pipelineId: string, limit?: number): ExecutionStreamState

useExecutionStream renders one pipeline's live run events as they happen — execution start and finish plus per-node progress, newest first, at most limit (default 200). Needs pipelines:read (pipelines:execute carries it). Pair it with the executions read API for history: only manual and API-submitted runs stream live; scheduled and message-triggered runs don't emit these events. An empty pipelineId renders an idle stream ('closed', no error) so a not-yet-bound template stays honest.

Example

const run = useExecutionStream(pipelineId);
return run.error ? <ErrorNotice error={run.error} /> : <Timeline items={run.events.map((e) => ({ at: e.endedAt ?? e.startedAt ?? '', title: e.type, tone: e.type === 'execution.failed' ? 'critical' : 'ok' }))} />;

usePermission​

function usePermission(scope: string): { granted: boolean; reason?: string; }

usePermission reports whether the current VIEWER holds a scope the app's manifest requested — the FR-29/42 data every bound actionable control renders its state from. Unknown scope → not granted, with a reason (fail closed, and the UI still explains itself).

useTopic​

function useTopic(topic: string): TopicState

useTopic renders the latest value on a topic; status/error explain the gaps.

Example

const t = useTopic('plant/line1/oee');
return t.error ? <ErrorNotice error={t.error} /> : <MetricCard label="OEE" value={t.value ?? '—'} />;

Components​

AppProvider​

function AppProvider({ children }: { children: ReactNode; }): JSX.Element

AppProvider connects the React tree to the bridge state. When the shell disables the app mid-session (kill switch, FR-21) it also covers the whole frame: a read-only wall dashboard must not keep showing stale numbers behind a token that no longer exists. Plain inline styles — the SDK does not depend on @maestrohub/app-ui.

Functions​

ago​

function ago(iso?: string | null): string

ago renders how long ago an ISO-8601 instant was — "12s ago", "3m ago", "2h ago" — and "—" when there is no instant.

apiFetch​

function apiFetch<T = unknown>(path: string, init?: RequestInit): Promise<T>

apiFetch calls a platform API path (e.g. "/api/v1/uns/topics") with the current app token. Throws ApiError on refusals — including the case the UI should treat as "render disabled-with-reason", a 403 INSUFFICIENT_SCOPE.

fmt​

function fmt(n: unknown, digits?: number): string

fmt renders a number for a tile or a cell: integers as they are, anything else with digits decimals (default 1), and "—" for anything that is not a finite number.

isConfirmationRequired​

function isConfirmationRequired(err: unknown): err is ApiError

isConfirmationRequired: the platform wants the viewer's confirmation first.

isRateLimited​

function isRateLimited(err: unknown): err is ApiError & { status: 429; }

isRateLimited: the platform refused because this app (all of its viewers together) exceeded its request budget. Back off for retryAfterSeconds; prefer useTopic/useTopics for live values instead of polling REST.

mountApp​

function mountApp(App: React.ComponentType): void

mountApp is the boot contract: the served document loads boot.mjs, which imports the app's default component and calls this. The bridge handshake completes BEFORE first render, so hooks never see a half-connected state; uncaught errors report to the shell (NR-3/FR-8).

num​

function num(value: unknown, field?: string): number | null

num reads the numeric reading out of a topic value — a number, a numeric string, or an object carrying one under field (default "value") — the shapes a UNS record's value takes depending on how the connector published it. null when there is no number.

parsePayload​

function parsePayload(raw: string): Pick<TopicMessage, "value" | "timestamp" | "quality" | "source">

parsePayload mirrors the shell's data explorer: enriched envelope → value; JSON → parsed; else raw.

subscribeTopic​

function subscribeTopic(topic: string, onMessage: (m: TopicMessage) => void, onError?: (e: LiveError) => void): () => void

subscribeTopic delivers every value published on topic until the returned function is called. Refusals (missing scope, topic outside the viewer's access, app disabled) arrive on onError with a reason and a remediation — never as silence.

Classes​

ApiError​

class ApiError extends Error
new ApiError(status: number, code: string, message: string, details?: unknown, retryAfterSeconds?: number, remediation?: string): ApiError

ApiError carries the platform's flat {code, message, details?} refusal shape. details is what a refusal hands back for the caller to act on — a storage 409 carries the winning document there.

Members

  • readonly code: string
  • readonly details?: unknown
  • readonly remediation?: string | undefined — The platform's own "what to do instead" — every refusal body carries one (rule 36).
  • readonly retryAfterSeconds?: number | undefined — Seconds the platform asked us to wait (429 APP_RATE_LIMITED, Retry-After).
  • readonly status: number

ConflictError​

class ConflictError<T = unknown> extends Error
new ConflictError<T = unknown>(current: StoredDocument<T> | null): ConflictError<T>

Thrown when a CAS write cannot converge; carries the winner's document.

Members

  • readonly current: StoredDocument<T> | null

DownloadRefusedError​

class DownloadRefusedError extends Error
new DownloadRefusedError(reason: string): DownloadRefusedError

DownloadRefusedError is what bridge.download rejects with when the file could not be handed to the viewer — locally (over MAX_DOWNLOAD_CHARS) or by the shell (download_refused). .reason is the human-readable explanation the app should show (rule 36).

Members

  • readonly reason: string

Constants​

ALERTS_FEED​

const ALERTS_FEED: "alerts"

The pseudo-topic the alarm feed reports under in LiveError.topic.

CODE_ACTION_NOT_DECLARED​

const CODE_ACTION_NOT_DECLARED: "ACTION_NOT_DECLARED"

ApiError.code when an app-token request hits one of the three write doors (publish, connector execute, pipeline submit) without a matching declared action in the app's manifest. The fix is a manifest change and a republish, not a retry.

CODE_APPROVAL_REQUIRED​

const CODE_APPROVAL_REQUIRED: "APPROVAL_REQUIRED"

ApiError.code when the matched manifest action is on the "approval" rung: the write guard refuses direct calls to it — such an action only runs through a pipeline with an approval gate.

CODE_CONFIRMATION_REQUIRED​

const CODE_CONFIRMATION_REQUIRED: "CONFIRMATION_REQUIRED"

ApiError.code when the platform's write guard requires the viewer's confirmation for a rung-"confirm" action and the request carried no confirmed-at assertion. Retry the same call with { confirmed: true } from the confirmed click; isConfirmationRequired checks for it.

CODE_RATE_LIMITED​

const CODE_RATE_LIMITED: "APP_RATE_LIMITED"

CODE_RATE_LIMITED: the app's per-org fair-use budget is spent (429).

MAX_DOWNLOAD_CHARS​

const MAX_DOWNLOAD_CHARS: 20000000

The size cap (in characters) on a download message's content. bridge.download / bridge.requestDownload refuse locally above it, and the shell (AppFrameHost) answers download_refused with the same limit for anything that slips through.

PROTOCOL_VERSION​

const PROTOCOL_VERSION: 1

The bridge schema version stamped as v on every message in both directions. The app-side bridge ignores any shell message whose v differs, and the shell does the same for app messages.

SCOPE_ALERTS_ACK​

const SCOPE_ALERTS_ACK: "alerts:ack"

The scope alerts.ack needs; guard its button with usePermission(SCOPE_ALERTS_ACK).

SCOPE_APPROVALS_DECIDE​

const SCOPE_APPROVALS_DECIDE: "approvals:decide"

The scope approvals.approve / approvals.reject need; guard their buttons with it.

SCOPE_APPROVALS_READ​

const SCOPE_APPROVALS_READ: "approvals:read"

The scope the approvals inbox reads need (approvals:decide carries it too).

SCOPE_FUNCTIONS_DISCOVER​

const SCOPE_FUNCTIONS_DISCOVER: "functions:discover"

The scope connectors.execute needs for a discover-effect function (browse namespaces, enumerate operations).

SCOPE_FUNCTIONS_READ​

const SCOPE_FUNCTIONS_READ: "functions:read"

Per-effect function scopes: guard a button with the one matching the function's effect.

SCOPE_FUNCTIONS_STREAM​

const SCOPE_FUNCTIONS_STREAM: "functions:stream"

The scope connectors.execute needs for a stream-effect function (subscribe, tail, watch).

SCOPE_FUNCTIONS_WRITE​

const SCOPE_FUNCTIONS_WRITE: "functions:write"

The scope connectors.execute needs for a write-effect function (commands, tag writes, publishes).

SCOPE_PIPELINES_EXECUTE​

const SCOPE_PIPELINES_EXECUTE: "pipelines:execute"

The scope pipelines.submit needs; guard its button with usePermission(SCOPE_PIPELINES_EXECUTE).

SCOPE_PIPELINES_READ​

const SCOPE_PIPELINES_READ: "pipelines:read"

The scope the execution stream needs — pipelines:execute carries read, so an app that submits runs can also watch them land.

SCOPE_PLANT_READ​

const SCOPE_PLANT_READ: "entities:read"

The scope every plant.* read needs; guard plant panels with usePermission(SCOPE_PLANT_READ). The scope string stays entities:read — manifests already declare it.

SCOPE_UNS_PUBLISH​

const SCOPE_UNS_PUBLISH: "uns:publish"

Scope names the actions need — exported so apps guard with the same strings.

SCOPE_UNS_READ​

const SCOPE_UNS_READ: "uns:read"

The scope live subscriptions need. live.subscribe checks the viewer's grant for it before opening a socket and reports scope_missing on onError when it is absent; guard live panels with usePermission(SCOPE_UNS_READ).

alerts​

const alerts: { recent(options?: RecentAlertsOptions): Promise<AlertItem[]>; history(options?: AlertHistoryOptions): Promise<AlertHistoryItem[]>; subscribe: (sub: Parameters<typeof live.subscribeAlerts>[0]) => () => void; ack(input: AckAlertInput, options?: ActionOptions): Promise<AckAlertResult>; }

The alarm surface: recent and history read the platform's alert feed (needs uns:read — alert visibility inherits topic read), subscribe streams transitions live (see useAlerts), and ack acknowledges one alarm through the write ladder — a declared alerts.ack action bound to an alerts binding, with its rung — and needs alerts:ack. The viewer's own alert:ack authority per topic still bounds it: an app never silences an alarm its viewer could not.

Members

  • ack: (input: AckAlertInput, options?: ActionOptions) => Promise<AckAlertResult> — Acknowledge one alarm with the viewer's authority. The answer says what it did: not_found when no active alarm carries that key.
  • history: (options?: AlertHistoryOptions) => Promise<AlertHistoryItem[]> — One topic's (or every topic's) transitions, newest first.
  • recent: (options?: RecentAlertsOptions) => Promise<AlertItem[]> — Alarms currently known to the platform (active, cleared-recently, shelved), newest first.
  • subscribe: (sub: Parameters<typeof live.subscribeAlerts>[0]) => () => void — Live transitions over the app's scoped socket (needs uns:read). See useAlerts.

approvals​

const approvals: { list(options?: ListApprovalsOptions): Promise<ListApprovalsResponse>; get(id: string): Promise<ApprovalDecision>; approve(input: DecideApprovalInput, options?: ActionOptions): Promise<ApprovalDecision>; reject(input: DecideApprovalInput, options?: ActionOptions): Promise<ApprovalDecision>; }

The approvals surface: the human decision inbox for pipeline gates. list / get read it (approvals:read; approvals:decide carries read); approve / reject decide one — the pipeline then continues with the held payload, or stops — through the write ladder (a declared approvals.decide action bound to an approvals binding, with its rung) and with the viewer's own approval:decide authority. There is no live stream for approvals; poll list gently (the inbox changes at human speed) or lean on the deadline the row carries.

Members

  • approve: (input: DecideApprovalInput, options?: ActionOptions) => Promise<ApprovalDecision> — Approve: the pipeline continues with the held payload, as the viewer.
  • get: (id: string) => Promise<ApprovalDecision> — One decision, with its payload.
  • list: (options?: ListApprovalsOptions) => Promise<ListApprovalsResponse> — The inbox, newest first; state defaults to "pending" server-side.
  • reject: (input: DecideApprovalInput, options?: ActionOptions) => Promise<ApprovalDecision> — Reject: the pipeline continues with decision=rejected, as the viewer.

bridge​

const bridge: Bridge

The module-singleton bridge — one iframe, one bridge.

Members

  • currentState: () => AppContextState | null
  • currentToken: () => string | null — currentToken returns the live bearer value or null (disabled/expired-out).
  • download: (name: string, mime: string, content: string) => Promise<void> — download is the promise form of requestDownload: it settles once the shell has had its say. Rejects with a DownloadRefusedError (carrying .reason) when the content is over MAX_DOWNLOAD_CHARS or the shell answers download_refused; resolves after DOWNLOAD_REFUSAL_WINDOW_MS without a refusal. Refusals are only received after start() has run (the listener is the handshake's) — mountApp always does that.
  • post: (msg: AppToShellMessage) => void
  • reportError: (message: string, stack?: string) => void — reportError sends an uncaught error to the shell (NR-3 / FR-8).
  • reportLiveError: (e: { code: string; reason: string; remediation?: string; topic?: string; }) => void — reportLiveError tells the shell a live subscription failed. The builder shows it next to the preview with the remediation; the Run and Kiosk hosts ignore it (the app's own UI already told the viewer).
  • reportTelemetry: (method: string, path: string, status: number, durationMs: number) => void — reportTelemetry sends one API call's timing to the shell (G7).
  • requestDownload: (name: string, mime: string, content: string) => boolean — requestDownload asks the shell to hand the viewer a file (the sandbox cannot start downloads itself). Text content only; refused (returns false) above MAX_DOWNLOAD_CHARS. Synchronous, fire-and-forget: a refusal the SHELL decides on later is logged, not returned — callers that need to react to it use download.
  • start: () => Promise<AppContextState> — start runs the handshake once; subsequent calls return the same promise.
  • subscribe: (fn: Listener) => () => void

connectors​

const connectors: { execute(input: ExecuteFunctionInput, options?: ActionOptions): Promise<ExecuteFunctionResult>; }

The connector-function surface: execute runs a saved function on a connection with the viewer's authority (see the member docs). Rides the write ladder and needs the functions:<effect> scope of that function.

Members

  • execute: (input: ExecuteFunctionInput, options?: ActionOptions) => Promise<ExecuteFunctionResult> — Execute a saved connector function (an OPC UA write, a DB query, a command) with the viewer's authority. The platform resolves the function's effect and demands the matching functions:<effect> scope.

data​

const data: { subscribe: (topic: string, onMessage: (m: TopicMessage) => void, onError?: (e: LiveError) => void) => () => void; publish(input: PublishInput, options?: ActionOptions): Promise<PublishResult>; }

The UNS data surface: subscribe for live values (see subscribeTopic) and publish for one write to an existing topic (see the member docs). publish goes through the write ladder — declared action, idempotency key, in-flight lock — and needs uns:publish.

Members

  • publish: (input: PublishInput, options?: ActionOptions) => Promise<PublishResult> — Publish one value to an existing UNS topic.
  • subscribe: (topic: string, onMessage: (m: TopicMessage) => void, onError?: (e: LiveError) => void) => () => void — Live values on a topic over the app's scoped socket (needs uns:read). See subscribeTopic.

live​

const live: LiveConnection

The app's single live-data connection (one socket per runtime), shared by useTopic, subscribeTopic and data.subscribe. Call live.subscribe(topic, subscriber) directly only when you need the onStatus callback; see the LiveConnection member docs.

Members

  • alertSubscriberCount: () => number — Test seam: number of alarm-feed subscribers.
  • pipelineCount: () => number — Test seam: number of pipelines with live watchers.
  • subscribe: (topic: string, sub: TopicSubscriber) => () => void
  • subscribeAlerts: (sub: AlertSubscriber) => () => void — subscribeAlerts delivers every alarm transition in the organization the viewer may see (alert visibility inherits topic read) until the returned function is called. Needs uns:read like a topic; refusals arrive on onError with a reason and a remediation.
  • subscribePipeline: (pipelineId: string, sub: PipelineSubscriber) => () => void — subscribePipeline delivers ONE pipeline's live execution events — execution.started/completed/failed/cancelled plus per-node progress — until the returned function is called. Gated exactly like the HTTP pipeline reads: the pipelines:read scope (pipelines:execute carries it) plus the viewer's own access to that pipeline; refusals arrive on onError with a reason and a remediation. Only manual and API-submitted runs emit events (mode 'live' | 'sandbox'); scheduled and message-triggered runs appear in the executions API.
  • topicCount: () => number — Test seam: number of topics with live subscribers.

notifications​

const notifications: { notify(input: NotifyInput, options?: ActionOptions): Promise<NotifyResult>; }

The notification surface: notify posts one notice to the person feed of THE APP'S OWN PEOPLE — its owner plus every viewer who has opened it. The platform stamps the rest: the producer, the app-name prefix on the title, and the audience — an app can neither impersonate the platform nor reach beyond its users. A declared manifest action (kind notifications.notify, a notifications binding, a rung) — no scope needed. Org channels (Slack, Teams) hear app notices only when an operator explicitly subscribes a channel to them.

Members

  • notify: (input: NotifyInput, options?: ActionOptions) => Promise<NotifyResult> — Post one notice to the app's people.

pipelines​

const pipelines: { submit(input: SubmitPipelineInput, options?: ActionOptions): Promise<SubmitPipelineResult>; }

The pipeline surface: submit fires a pipeline's manual-trigger node with the viewer's authority (see the member docs). Rides the write ladder and needs pipelines:execute.

Members

  • submit: (input: SubmitPipelineInput, options?: ActionOptions) => Promise<SubmitPipelineResult> — Fire a pipeline's manual trigger with the viewer's authority.

plant​

const plant: { whatCanIAsk(): Promise<Envelope<Vocabulary>>; find(options: FindOptions): Promise<Envelope<Candidate[]>>; describe(thing: string, options?: DescribeOptions): Promise<Envelope<Described>>; walk(options: WalkOptions): Promise<Envelope<WalkedThing[]>>; numbers<T = unknown>(options: NumbersOptions): Promise<Envelope<T>>; timeline(options: TimelineOptions): Promise<Envelope<TimelineEntry[]>>; }

The plant — read-only. Everything needs the entities:read scope (SCOPE_PLANT_READ); the viewer's own access bounds every row, as always. Each call is one of the knowledge module's tools, answered as an Envelope: read result, and say what coverage and hidden say. Key per-machine app state by the thing's id — a topic path renames. Nothing here writes: a change to the plant is a proposal a person decides under Review, on the platform's own pages.

Members

  • describe: (thing: string, options?: DescribeOptions) => Promise<Envelope<Described>> — One thing in full: its facts, places, identities, bindings (the topics), and with "values" what it reads now.
  • find: (options: FindOptions) => Promise<Envelope<Candidate[]>> — Things by name, by a number another system issued, or by the topic(s) they are readings of.
  • numbers: <T = unknown>(options: NumbersOptions) => Promise<Envelope<T>> — A figure for one thing or hundreds at once — the latest value, an average, time in a state, OEE — ordered when order is set: the "N machines, worst first" question. The result's shape follows the measure.
  • timeline: (options: TimelineOptions) => Promise<Envelope<TimelineEntry[]>> — What happened to a thing, or what is happening now under a place: alarms, trips, work orders, shifts, calibrations.
  • walk: (options: WalkOptions) => Promise<Envelope<WalkedThing[]>> — Everything reached from a thing — down a tree (the machines under a line) or along links (what a pump feeds) — with their readings when values is set.
  • whatCanIAsk: () => Promise<Envelope<Vocabulary>> — The plant's vocabulary: its sites (the roots of every tree), the trees it keeps, its types, what it measures. Ask it first.

storage​

const storage: { shared: { collection: <T>(name: string) => Collection<T>; }; user: { collection: <T>(name: string) => Collection<T>; }; }

The app's own document store: shared (one copy per org, CAS by default) and user (one copy per viewer) — both expose collection(name) returning a Collection. Not for plant data; see the Collection member docs for get/put/delete/list/usage.

Example

const prefs = storage.user.collection<{ unit: string }>('prefs');
await prefs.put('display', { unit: 'bar' }, (current, mine) => ({ ...current, ...mine }));

Members

  • shared: { collection: <T>(name: string) => Collection<T>; } — App-wide documents (the manifest's scope: "shared" collections).
  • user: { collection: <T>(name: string) => Collection<T>; } — Per-viewer documents (the manifest's scope: "user" collections).

Types​

AckAlertInput​

interface AckAlertInput

Input to alerts.ack — the alarm's identity as the feed returns it (topicId + level, plus propertyId / direction when the feed carries them). The property is named by id: the platform keys an incident by id so a rename never splits it, and a name is not a key. Mirrors uns AcknowledgeAlertRequest verbatim.

Members

  • direction?: string | undefined
  • level: string
  • propertyId?: string | undefined
  • topicId: string

ActionOptions​

interface ActionOptions

Options every write action accepts.

Members

  • confirmed?: boolean | undefined — The viewer confirmed this specific invocation (rung "confirm").
  • idempotencyKey?: string | undefined — Reuse a key to retry the SAME request; omit for a fresh invocation.

AlarmState​

interface AlarmState

A live alarm's state, as plant.timeline reports it with state set.

Members

  • acknowledged: boolean
  • acknowledged_at?: string | undefined
  • acknowledged_by_name?: string | undefined
  • active: boolean
  • active_hours: number
  • shelved: boolean
  • shelved_until?: string | undefined
  • standing: boolean
  • to_act_on: boolean

AlertEvent​

interface AlertEvent

One alarm transition from the platform's rule engine — mirrors the uns contract SchemaAlertFiredEvent (shared/contracts/uns) the socket relays as an alert.fired frame. cleared false = the alarm fired, true = it cleared.

Members

  • attributePath?: string | undefined — The property's name at the transition: a label, never a key.
  • cleared: boolean
  • direction: string — "low" | "high".
  • firedAt: string — RFC3339.
  • level: string — "warning" | "critical" — the threshold that tripped.
  • message?: string | undefined
  • propertyId?: string | undefined — The alarm key's property, by id. Empty for value schemas.
  • receivedAt: number
  • recordTimestamp: string
  • schemaId: string
  • schemaName: string
  • severity: string — Operator-facing "info" | "warning" | "critical" from the alert config.
  • threshold: number
  • topicId: string
  • topicPath: string
  • unit?: string | undefined
  • value: number

AlertHistoryOptions​

interface AlertHistoryOptions

Options for alerts.history: one topic's transitions, newest first, before an instant.

Members

  • before?: string | undefined — RFC3339 — only events before this instant.
  • limit?: number | undefined
  • topicId?: string | undefined

AlertItem​

interface AlertItem

One alarm as the platform's feed returns it — mirrors uns CurrentAlertDTO verbatim.

Members

  • acknowledgedAt?: string | undefined
  • attributePath?: string | undefined — The property's name at the last fire: a label, never a key.
  • clearedAt?: string | undefined
  • direction?: string | undefined — "low" | "high".
  • fireCount: number
  • firstFiredAt: string — RFC3339.
  • lastClearedAt?: string | undefined
  • lastFiredAt: string
  • lastRecordTs?: string | undefined
  • level: string — "warning" | "critical" — the threshold that tripped.
  • message?: string | undefined
  • orgId: string
  • propertyId?: string | undefined — The alarm key's property, by id (empty for a value schema).
  • schemaId: string
  • schemaMajor: number
  • schemaMinor: number
  • schemaName: string
  • severity: string — Operator-facing "info" | "warning" | "critical".
  • shelvedAt?: string | undefined
  • shelvedBy?: string | undefined
  • shelvedUntil?: string | undefined
  • threshold: number
  • topicId: string
  • topicPath: string
  • unit?: string | undefined
  • value: number

AlertSubscriber​

interface AlertSubscriber

Callbacks handed to live.subscribeAlerts; useAlerts builds one for you.

Members

  • onError?: ((e: LiveError) => void) | undefined
  • onEvent: (e: AlertEvent) => void
  • onStatus?: ((s: LiveStatus) => void) | undefined

AlertsState​

interface AlertsState

What useAlerts returns: the latest transitions (newest first) plus the feed's status.

Members

  • error: LiveError | null
  • events: AlertEvent[]
  • status: LiveStatus

AppContextState​

interface AppContextState

What the shell told the app about its viewer, as read by useAppContext and bridge.currentState. Built from the context message; theme follows later theme pushes and disabled is set by a disable push.

Members

  • canEditApp: boolean — The viewer may edit this app — gate curation UI on it (writers: "editors" collections).
  • disabled?: string | undefined — Set once the shell disabled the app; carries the notice to show. The token is dropped at the same time.
  • org: { id: string; slug: string; } — The organisation the app runs in.
  • permissions: Record<string, { granted: boolean; reason?: string; }> — Per-scope grant results; usePermission reads these.
  • theme: ThemeState — The shell's current theme mode.
  • user: { id: string; displayName: string; } — The viewer's id and display name.

AppToShellMessage​

type AppToShellMessage = HelloMessage | ReadyMessage | ErrorMessage | TelemetryMessage | DownloadMessage | LiveErrorMessage;

Every message the app posts to the shell: hello (handshake start), ready (handshake done), error (uncaught error), telemetry (one API call's timing), download (hand the viewer a file) and live_error (a live-data subscription failed, for the builder). Discriminated on type.

ApprovalDecision​

interface ApprovalDecision

One pending or decided approval — mirrors the engine's DecisionResponse verbatim.

Members

  • createdAt: string
  • deadline: string — RFC3339 — the gate expires by itself after this.
  • decidedAt?: string | undefined
  • decidedBy?: string | undefined
  • decisionNote?: string | undefined
  • freshUntil?: string | undefined
  • id: string
  • kind: string — Apps see "pipeline_gate" only — the agent-writes surface is not exposed.
  • nodeId: string
  • payload?: unknown — The held upstream data — exactly what continues on approve.
  • pipelineId: string
  • pipelineName?: string | undefined
  • reason?: string | undefined
  • requestedBy?: string | undefined
  • signature: string
  • sourceExecutionId?: string | undefined
  • state: string — "pending" | "approved" | "rejected" | "expired" | "executed".
  • title: string

Binding​

interface Binding

A binding: which series carries one property of a thing.

Members

  • id: string
  • property_id: string
  • series: { kind: string; ref: string; org_id?: string; } — kind "uns" and ref the topic path — THE way from a thing to its live data.
  • thing_id: string
  • unit?: string | undefined

Candidate​

interface Candidate

plant.find: one thing that answers.

Members

  • name: string
  • physical_place?: string | undefined — Where it sits in the physical tree, in words.
  • property?: string | undefined
  • reading?: Reading | undefined
  • site: string
  • thing: string
  • topic?: string | undefined — With topic / topics: the topic the row answers for, and the property it is bound to.
  • type?: string | undefined

Collection​

interface Collection<T = unknown>

A handle on one declared storage collection, from storage.shared.collection(name) or storage.user.collection(name). Every method is one or more apiFetch calls to /api/v1/appstudio/storage/<collection>; an undeclared collection is a 404.

Members

  • delete: (key: string) => Promise<void> — Removes a document; resolves even if it was already gone.
  • get: (key: string) => Promise<StoredDocument<T> | null> — Returns null when absent.
  • list: (opts?: ListOptions) => Promise<ListPage<T>> — One page of documents; follow next_cursor for more.
  • put: (key: string, value: T, merge?: (current: T | null, mine: T) => T) => Promise<StoredDocument<T>> — Writes a value. On a CAS collection: merges onto the current document via merge (default: replace) and retries until it lands or MAX_CAS_ATTEMPTS is hit. On a last-write-wins collection: one write.
  • usage: () => Promise<UsageInfo> — Document count, bytes used and the cap for this collection's scope.

ContextMessage​

interface ContextMessage

Shell → app: identity, per-scope grant results, theme, and the token.

Members

  • canEditApp?: boolean | undefined — The viewer holds app:update on this app (a "keeper" — App Data Authority R1): editor-only affordances render enabled from it. The platform re-checks server-side on every write.
  • org: { id: string; slug: string; }
  • permissions: Record<string, { granted: boolean; reason?: string; }> — Per-scope grant results from the token exchange (granted + denied_scopes) — what usePermission renders disabled-with-reason from (FR-29/42).
  • theme: ThemeState
  • token: TokenPayload
  • type: "context"
  • user: { id: string; displayName: string; }
  • v: 1

DecideApprovalInput​

interface DecideApprovalInput

Input to approvals.approve / approvals.reject.

Members

  • id: string — The decision's id, from the inbox.
  • note?: string | undefined — Recorded beside the verdict; the decider is always the viewer.

DescribeOptions​

interface DescribeOptions

What plant.describe may bring beyond the thing itself.

Members

  • as_of?: string | undefined — The thing as it was at an instant, RFC3339.
  • include?: ("values" | "links" | "ended" | "missing" | "running" | "brief")[] | undefined — "values" for what it reads now, "links" for its links, "missing" for recorded absences.
  • property?: string | undefined — Narrow the readings to one property.

Described​

interface Described

plant.describe: one thing in full.

Members

  • bindings?: Binding[] | undefined — The series bound to it — series.ref is the topic path for live values.
  • declared?: InForce | undefined
  • documents?: string[] | undefined
  • facts?: Record<string, FactValue> | undefined
  • identities?: Identity[] | undefined
  • links?: DescribedLink[] | undefined
  • parts?: string[] | undefined
  • places?: Record<string, string> | undefined — Where it sits, in every tree that holds it, keyed by the tree kind (physical, location, floc, …).
  • readings?: Reading[] | undefined — With include "values": what it reads now.
  • thing: Thing
  • type_chain?: string[] | undefined — Its type and every type it extends, nearest first.
interface DescribedLink

A link on a thing, named from the thing's side.

Members

  • link: string
  • name: string
  • other: string
  • other_name: string
  • other_site?: string | undefined
  • since: string
  • until?: string | undefined

DisableMessage​

interface DisableMessage

Shell → app: kill switch / unshare — drop the token, show the notice.

Members

  • message: string
  • type: "disable"
  • v: 1

DownloadMessage​

interface DownloadMessage

App → shell: hand the viewer a file (G1 export). The sandbox has no allow-downloads, so a download started inside the iframe is inert; the shell materialises it. Text content only (CSV/JSON/text), size-capped.

Members

  • content: string
  • id?: string | undefined — Correlates a download_refused reply with the request that caused it.
  • mime: string
  • name: string
  • type: "download"
  • v: 1

DownloadRefusedMessage​

interface DownloadRefusedMessage

Shell → app: the shell refused a download (over MAX_DOWNLOAD_CHARS, non-text content). id echoes the request's id so the SDK can settle the right caller's promise; a refusal is never silent (rule 36).

Members

  • id?: string | undefined
  • reason: string
  • type: "download_refused"
  • v: 1

Envelope​

interface Envelope<T>

Every plant answer: the result, and what the planner says about it — what it could not read (coverage), how many rows the viewer may not see (hidden), the sentence it computed, and the calls that would say more.

Members

  • ambiguous?: boolean | undefined — More than one thing answered to the name asked.
  • computed?: string | undefined — The answer's own sentence, in words.
  • coverage?: { missing?: string[]; without_reading?: string[]; } | undefined — What the answer needed and the plant does not record, or did not read.
  • cut_short?: { limit: string; } | undefined — The answer was cut short at this limit.
  • hidden?: number | undefined — Rows the viewer's access hides — counted, never shown.
  • next?: { tool: string; why: string; }[] | undefined — What to ask next, and why.
  • readings_as_of?: string | undefined — The instant the readings in the answer are as of, RFC3339.
  • result: T

ErrorMessage​

interface ErrorMessage

App → shell: an uncaught error (NR-3 + FR-8's send-to-agent hook).

Members

  • message: string
  • stack?: string | undefined
  • type: "error"
  • v: 1

ExecuteFunctionInput​

interface ExecuteFunctionInput

Input to connectors.execute.

Members

  • connectionId: string — The connection the saved function belongs to.
  • functionId: string — A SAVED function on that connection (apps cannot run ad-hoc function bodies).
  • params?: Record<string, unknown> | undefined — Function parameters; sent as {} when omitted.

ExecuteFunctionResult​

interface ExecuteFunctionResult

Mirrors the platform's FunctionResultResponse.

Members

  • data?: Record<string, unknown> | undefined
  • durationMs: number
  • error?: string | undefined
  • metadata?: Record<string, unknown> | undefined
  • success: boolean
  • timestamp: string

ExecutionEvent​

interface ExecutionEvent

One live event from a pipeline's execution stream — the platform's unified live-exec family (shared/contracts/pipeline live_events.go), delivered as WS frames typed execution.* / node.*. Field names mirror the contract's json tags verbatim (camelCase); a full-boot Go test pins them. Only manual and API-submitted runs emit these events (mode separates real runs from sandbox test runs); scheduled and message-triggered runs do not — read those from the executions API.

Members

  • data: Record<string, unknown> — The frame's full payload, verbatim, for fields not lifted above.
  • durationMs?: number | undefined
  • endedAt?: string | undefined
  • error?: string | undefined — A node event's error, or execution.failed's errorSummary.
  • executionId: string
  • mode: string — 'live' for a real run, 'sandbox' for a test run.
  • nodeId?: string | undefined — Node events only.
  • nodeName?: string | undefined
  • nodeType?: string | undefined
  • pipelineId: string
  • receivedAt: number
  • startedAt?: string | undefined
  • type: string — 'execution.started' | 'execution.completed' | 'execution.failed' | 'execution.cancelled' | 'node.started' | 'node.progress' | 'node.completed' | 'node.failed' | 'node.skipped' | 'node.recovered' | 'node.prompt'. node.progress is a loop body node's coalesced progress: cumulative completed / failed counts and the latest iteration's output.

ExecutionStreamState​

interface ExecutionStreamState

What useExecutionStream returns: the run events (newest first) plus the stream's status.

Members

  • error: LiveError | null
  • events: ExecutionEvent[]
  • status: LiveStatus

FactValue​

interface FactValue

A fact a thing carries, with its unit when it has one.

Members

  • unit?: string | undefined
  • value: unknown

FindOptions​

interface FindOptions

What plant.find takes: a name, a number another system issued, a topic, or many topics at once.

Members

  • as_of?: string | undefined — The plant as it was at an instant, RFC3339.
  • identifier?: string | undefined — A number another system issued, with system saying which.
  • name?: string | undefined — A name or part of one, in any language the plant records.
  • system?: string | undefined
  • topic?: string | undefined — A uns topic path: answered with the thing it is a reading of and the property.
  • topics?: string[] | undefined — Many topic paths in one call — a screen's whole list; never topic in a loop.

HelloMessage​

interface HelloMessage

App → shell: announce readiness to receive context.

Members

  • app: { slug: string; }
  • type: "hello"
  • v: 1

HostPalette​

interface HostPalette

The host's design tokens, resolved by the shell to plain CSS colours (hsl(220 14% 10%), #fff) so an app renders in the shell's exact theme — every variant, not a hard-coded light/dark pair. Optional in every message: a shell that cannot resolve them (no stylesheet yet) omits the field and the UI kit falls back to its own palette.

Members

  • background: string — Page background (--background).
  • border: string — Borders and rules (--border).
  • danger: string — Critical / destructive (--destructive).
  • font: string — The shell's font stack (--font-sans).
  • ink: string — Primary text (--foreground).
  • muted: string — Secondary text (--muted-foreground).
  • primary: string — Accent — primary buttons, active tab, default chart series (--primary).
  • primaryInk: string — Text on the accent (--primary-foreground).
  • radius: string — The shell's corner radius (--radius), a CSS length.
  • subtle: string — Subtle fill — table header, neutral button (--muted).
  • surface: string — Card / control surface (--card).

Identity​

interface Identity

A number another system knows the thing by: the SAP equipment, the functional location, the OPC UA node.

Members

  • id: string
  • identifier: string
  • kind?: string | undefined
  • system: string

InForce​

interface InForce

A declaration in force on a thing — its own or the nearest above it: maintenance, decommissioned, …

Members

  • by?: string | undefined
  • mode: string
  • note?: string | undefined
  • on: string — The thing the declaration was made on.
  • on_name?: string | undefined
  • since: string

ListApprovalsOptions​

interface ListApprovalsOptions

Filters for approvals.list — mirrors the engine's query names verbatim.

Members

  • limit?: number | undefined
  • offset?: number | undefined
  • pipelineId?: string | undefined
  • state?: string | undefined — "pending" (the default) | "approved" | "rejected" | "expired" | "executed" | "all".

ListApprovalsResponse​

interface ListApprovalsResponse

The inbox page shape.

Members

  • items: ApprovalDecision[]
  • total: number

ListOptions​

interface ListOptions

Query options for Collection.list; each one becomes a query parameter only when set.

Members

  • cursor?: string | undefined — The next_cursor of the previous page.
  • limit?: number | undefined — Maximum documents per page.
  • since?: string | undefined — RFC3339 — only documents updated at or after this instant.

ListPage​

interface ListPage<T = unknown>

One page of Collection.list.

Members

  • items: StoredDocument<T>[] — The documents on this page.
  • next_cursor?: string | undefined — Pass as ListOptions.cursor to fetch the next page; absent on the last page.

LiveError​

interface LiveError

A refusal or failure on the live path — always carries a next step.

Members

  • code: string
  • reason: string
  • remediation: string
  • topic?: string | undefined

LiveErrorMessage​

interface LiveErrorMessage

App → shell: a live-data subscription was refused or failed (a missing scope, a topic the platform would not serve, an auth failure). The SDK already tells the app's own code through onError; this copy is for the BUILDER, which otherwise sees only "Status: error" inside the preview and cannot say why. Carries the same code / reason / remediation the app got, so the builder can teach the fix.

Members

  • code: string
  • reason: string
  • remediation?: string | undefined
  • topic?: string | undefined
  • type: "live_error"
  • v: 1

LiveStatus​

type LiveStatus = 'connecting' | 'live' | 'error' | 'closed';

Connection state a subscriber sees: connecting until the server acks the subscription (also while reconnecting), live once it has, error after a refusal or failure. closed is part of the type but the current LiveConnection never emits it.

NumbersOptions​

interface NumbersOptions

What plant.numbers takes: a set of things, a property, a measure, a window.

Members

  • as_of?: string | undefined — The plant and its readings as they were at an instant, RFC3339.
  • measure?: string | undefined — Which number: "latest", "avg", "sum", "min", "max", "delta", "count", "time_in_state", "oee", …
  • order?: "asc" | "desc" | undefined — Order the rows: "asc" or "desc".
  • property?: string | undefined — The property to read or rank, e.g. "mh:vibration".
  • set: SetOptions
  • top?: number | undefined — How many rows to keep; the rest are counted, not dropped.
  • window?: string | undefined — The period: "now", "current_shift", "yesterday", "last_7d", "this_month", a date, a month.

PipelineSubscriber​

interface PipelineSubscriber

Callbacks handed to live.subscribePipeline; useExecutionStream builds one for you.

Members

  • onError?: ((e: LiveError) => void) | undefined
  • onEvent: (e: ExecutionEvent) => void
  • onStatus?: ((s: LiveStatus) => void) | undefined

PlantEvent​

interface PlantEvent

An event the plant recorded or worked out: alarm, trip, work_order, mode_change, …

Members

  • at: string
  • cleared_at?: string | undefined
  • id: string
  • kind: string
  • name?: string | undefined
  • payload?: Record<string, unknown> | undefined
  • severity?: "info" | "warning" | "critical" | undefined
  • site_id: string
  • source?: string | undefined
  • thing_id: string

PublishInput​

interface PublishInput

Input to data.publish.

Members

  • source?: string | undefined — Optional source label recorded with the point.
  • topic: string — Existing UNS topic path, e.g. "factory/line1/reset".
  • value: unknown — JSON-serialisable value the topic's schema accepts.

PublishResult​

interface PublishResult

What POST /api/v1/uns/data/publish returns on success.

Members

  • message: string — The platform's human-readable acknowledgement.

Reading​

interface Reading

A reading of a thing now, as the planner words it: the value, when, how old, how far it can be trusted.

Members

  • age: string — How old it is, in words.
  • at: string — When it was read, RFC3339.
  • flat?: boolean | undefined
  • name?: string | undefined
  • property: string — The property id, e.g. "mh:vibration".
  • quality: "good" | "uncertain" | "bad"
  • ref: string — The series it comes from — for kind "uns", the topic path: what useTopic takes.
  • source?: string | undefined
  • stale?: boolean | undefined
  • text?: string | undefined
  • unit?: string | undefined
  • value?: number | undefined

ReadyMessage​

interface ReadyMessage

App → shell: handshake complete.

Members

  • type: "ready"
  • v: 1

RecentAlertsOptions​

interface RecentAlertsOptions

Options for alerts.recent: activeOnly keeps only alarms still firing; shelved only shelved ones.

Members

  • activeOnly?: boolean | undefined
  • limit?: number | undefined
  • shelved?: boolean | undefined

SetOptions​

interface SetOptions

Which things a plant.numbers or plant.timeline set covers, in match's words, or a plain list of ids.

Members

  • include_subtypes?: boolean | undefined
  • things?: string[] | undefined
  • type?: string | undefined
  • under?: string | undefined — Everything below this thing in the physical tree.

ShellToAppMessage​

type ShellToAppMessage = ContextMessage | TokenMessage | ThemeMessage | DisableMessage | DownloadRefusedMessage;

Every message the shell may post into the app iframe: the initial context, token rotations, theme flips, disable (kill switch / unshare) and download_refused. Discriminated on type.

SiteClock​

interface SiteClock

A site's clock.

Members

  • name: string
  • now: string
  • site: string
  • zone: string

StoredDocument​

interface StoredDocument<T = unknown>

One document as the storage API returns it.

Members

  • key: string — The document key within its collection.
  • revision: number — Server-assigned revision; put sends it back for compare-and-set writes.
  • updated_at: string — RFC3339 instant of the last write.
  • updated_by: string — The user who last wrote the document.
  • value: T — The stored JSON value.

SubmitPipelineInput​

interface SubmitPipelineInput

Input to pipelines.submit.

Members

  • data?: Record<string, unknown> | undefined — Payload delivered to the run as its trigger data.
  • nodeId: string — The manual-trigger node's id inside that pipeline.
  • pipelineId: string — The pipeline that owns the manual-trigger node.

SubmitPipelineResult​

interface SubmitPipelineResult

What the scheduler's trigger endpoint returns on success.

Members

  • status: string — The scheduler's status word for the accepted trigger.

TelemetryMessage​

interface TelemetryMessage

App → shell: one API call's timing (G7). The builder aggregates these into "slow data sources" so latency is a build-time signal, not a production surprise. Sent for every apiFetch; cheap, fire-and-forget.

Members

  • durationMs: number
  • method: string
  • path: string
  • status: number
  • type: "telemetry"
  • v: 1

ThemeMessage​

interface ThemeMessage

Shell → app: a live theme switch (FR-17). The context message carries the initial mode; this one follows every flip the viewer makes in the shell, so an app never has to reload to match its host.

Members

  • theme: ThemeState
  • type: "theme"
  • v: 1

ThemeState​

interface ThemeState

The theme as the shell sends it: the mode, and the host tokens when the shell could resolve them.

Members

  • mode: "light" | "dark"
  • palette?: HostPalette | undefined

Thing​

interface Thing

One thing the plant knows: a machine, a line, an area, a site.

Members

  • facts?: Record<string, FactValue> | undefined
  • id: string
  • labels?: Record<string, string> | undefined
  • name: string
  • org_id?: string | undefined
  • site_id: string
  • type_id?: string | undefined — Its type, e.g. "company:centrifugal_pump".
  • validity?: { valid_from?: string; valid_to?: string; } | undefined

TimelineEntry​

interface TimelineEntry

One entry of plant.timeline: the event and the thing it happened to.

Members

  • alarm?: AlarmState | undefined
  • event: PlantEvent
  • site_name?: string | undefined
  • thing_name: string
  • why?: string | undefined — With rank: why it comes first.

TimelineOptions​

interface TimelineOptions

What plant.timeline takes: whose events, of which kinds, over which period or state.

Members

  • as_of?: string | undefined — The model as it was at an instant, RFC3339.
  • kinds?: string[] | undefined — Which kinds of event: "alarm", "trip", "work_order", "mode_change", "due", …; leave it out for all.
  • limit?: number | undefined
  • rank?: boolean | undefined — Order by what to look at first — severity, then how much sits downstream — rather than by time.
  • state?: "active" | "to_act_on" | "acknowledged" | "shelved" | "standing" | undefined — Live alarms ("active"), or only those to act on, acknowledged, shelved, standing.
  • things?: string[] | undefined — The things to ask about, by id or by name.
  • under?: string | undefined — Everything below this thing in the physical tree.
  • window?: string | undefined — The period: "now", "current_shift", "yesterday", "last_7d", "last_24h", a date, a month.

TokenMessage​

interface TokenMessage

Shell → app: a rotated token (silent re-issue, DS4).

Members

  • token: TokenPayload
  • type: "token"
  • v: 1

TokenPayload​

interface TokenPayload

The scoped app token the shell hands over in context and re-issues in token pushes.

Members

  • expiresAt: string — RFC3339 — the shell re-issues before this; the app never refreshes itself.
  • value: string — The bearer value apiFetch and the live socket authenticate with.

TopicMessage​

interface TopicMessage

One value delivered on a topic.

Members

  • quality?: number | undefined
  • raw: string — The payload exactly as the server sent it.
  • receivedAt: number
  • retain: boolean
  • source?: string | undefined
  • timestamp?: string | undefined — Server-side fields of the enriched envelope (application.MQTTPayload) when present.
  • topic: string
  • value: unknown — The parsed value: the enriched envelope's value, else parsed JSON, else the raw string.

TopicState​

interface TopicState

What useTopic returns; status and error explain why value may be null.

Members

  • error: LiveError | null — The last refusal or failure; cleared by the next message.
  • message: TopicMessage | null — The full last message (raw payload, retain flag, envelope fields), or null before the first.
  • status: LiveStatus — Current connection state for this topic.
  • value: unknown — The latest value (the enriched envelope's value), or null before the first message.

TopicSubscriber​

interface TopicSubscriber

Callbacks handed to live.subscribe; useTopic and subscribeTopic build one for you.

Members

  • onError?: ((e: LiveError) => void) | undefined — Refusals and failures (missing scope, refused topic, auth error, reconnecting, app disabled).
  • onMessage: (m: TopicMessage) => void — Every value delivered on the topic.
  • onStatus?: ((s: LiveStatus) => void) | undefined — Connection-state transitions for this subscription.

TreeView​

interface TreeView

A tree the plant keeps: a legal value of plant.walk's tree.

Members

  • default?: boolean | undefined
  • id: string
  • link_types?: string[] | undefined
  • name: string
  • tree_kind: string

TypeCount​

interface TypeCount

How many things are of a type.

Members

  • subtypes?: string[] | undefined
  • things: number
  • type: string
  • with_subtypes?: number | undefined

UsageInfo​

interface UsageInfo

What Collection.usage returns: how full the collection's scope is.

Members

  • cap_bytes: number — The quota; a write that would exceed it is refused with a 413.
  • doc_count: number — Number of documents stored.
  • total_bytes: number — Bytes used.

Vocabulary​

interface Vocabulary

plant.whatCanIAsk: the plant's vocabulary — its sites, trees, types, and what it measures.

Members

  • event_kinds?: string[] | undefined
  • link_types?: string[] | undefined
  • measured_properties: string[]
  • now: string
  • sites: SiteClock[]
  • trees: TreeView[]
  • types: TypeCount[]

WalkOptions​

interface WalkOptions

What plant.walk takes: a start, and either a tree or the link types to follow.

Members

  • as_of?: string | undefined — The plant as it was at an instant, RFC3339.
  • depth?: number | undefined — How many hops; leave it out to go as far as it goes.
  • direction?: "downstream" | "upstream" | "both" | "up" | "down" | undefined — Along links: "upstream" / "downstream" / "both"; in a tree: "up" / "down".
  • from: string — The thing to start at, by id or by name.
  • tree?: string | undefined — Follow a whole tree: "physical", "location", "floc", "electrical", "cost_centre" — whatCanIAsk lists this plant's.
  • type?: string[] | undefined — Return only these types, having walked through the rest.
  • values?: string[] | undefined — Properties to read for everything reached, in one read — the fleet's values, ranked by you.
  • via?: string[] | undefined — The link types to follow instead of a tree, e.g. ["feeds"].

WalkedThing​

interface WalkedThing

One thing a plant.walk reached.

Members

  • depth: number
  • name?: string | undefined
  • readings?: Reading[] | undefined — With values: the properties asked, read now.
  • site: string
  • stub?: boolean | undefined
  • thing: string
  • type?: string | undefined
  • via?: string[] | undefined — The link types followed to reach it.