/// /// /// import type { JSONSchema } from "zod/v4/core"; declare global { const siyuan: ISiyuan; } // ── Primitives ──────────────────────────────────────────────────────────────── /** * WebSocket connection ready-state values. * * @remarks Mirrors the browser `WebSocket.readyState` constants: * - `0`: CONNECTING * - `1`: OPEN * - `2`: CLOSING * - `3`: CLOSED */ export type TWebSocketReadyState = 0 | 1 | 2 | 3; /** * Server-Sent Events connection ready-state values. * * @remarks Mirrors the browser `EventSource.readyState` constants: * - `0`: CONNECTING * - `1`: OPEN * - `2`: CLOSED */ export type TEventSourceReadyState = 0 | 1 | 2; /** An absolute URL path that must start with `/`. */ export type TRequestPath = `/${string}`; /** A standard UUID in hyphenated 8-4-4-4-12 format. */ export type UUID = `${string}-${string}-${string}-${string}-${string}`; /** * A single directory entry returned by {@link IStorage.list}. */ export interface IStorageEntry { /** File or directory name (not the full path). */ name: string; /** `true` if this entry is a directory. */ isDir: boolean; /** `true` if this entry is a symbolic link. */ isSymlink: boolean; /** Last-modified time as a Unix timestamp (seconds since epoch). */ updated: number; } /** * A lazy data accessor returned by {@link IStorage.get} and {@link IFetchResponse}. * * @remarks Each method decodes the same underlying byte slice; call at most * once per method per instance. */ export interface IDataObject { /** * Decodes the data as a UTF-8 string. * * @returns The text content. */ text(): Promise; /** * Parses the data as JSON. * * @returns The parsed value. */ json(): Promise; /** * Returns the raw bytes as a node.js compatible `Buffer`. * * @returns The binary content. */ buffer(): Promise; /** * Returns the raw bytes as an `ArrayBuffer`. * * @returns The binary content. */ arrayBuffer(): Promise; } /** * Response object returned by {@link ISiyuan.fetch}. * * @remarks Extends {@link IDataObject} so the response body can be read * as text, JSON, or raw bytes. */ export interface IFetchResponse extends IDataObject { /** The final URL after any redirects. */ url: string; /** `true` when `status` is in the range 200–299. */ ok: boolean; /** HTTP status code, e.g. `200`. */ status: number; /** HTTP status text, e.g. `"OK"`. */ statusText: string; /** Response headers as a flat string-to-string map. */ headers: Record; } /** * Options accepted by {@link ISiyuan.fetch}. */ export interface IRequestInit { /** HTTP method. Defaults to `"GET"` when omitted. */ method?: string; /** Additional request headers. */ headers?: Record; /** Request body. Omit for methods that carry no body (e.g. GET, HEAD). */ body?: string | ArrayBuffer; } /** * An event message delivered to {@link IEvent.handler}. */ export interface IEventMessage { /** Unique event identifier. */ id: UUID; /** Event type name, e.g. `"ws"`. */ type: string; /** Event-specific payload; the shape depends on `type`. */ detail: any; } /** * Published on the runtime event bus after the plugin starts successfully * and enters the running state. */ export interface IStartEventMessage extends IEventMessage { type: 'start'; detail: null; } /** * Published on the runtime event bus at the beginning of a clean plugin * shutdown, before the runtime is torn down. */ export interface IStopEventMessage extends IEventMessage { type: 'stop'; detail: null; } /** File-system event kind emitted by the kernel storage watcher. */ export type TFsNotifyOperation = 'CREATE' | 'WRITE' | 'RENAME' | 'REMOVE'; /** * Published on the runtime event bus when a watched storage path is * created, written, renamed, or removed. * * Watching is managed via {@link IStorage.watcher}. */ export interface IFsNotifyEventMessage extends IEventMessage { type: 'fs-notify'; detail: { /** The type of file-system change that triggered this event. */ operation: TFsNotifyOperation; /** Path relative to the plugin's storage directory. */ path: string; } } export type TEventMessage = IStartEventMessage | IStopEventMessage | IFsNotifyEventMessage | IEventMessage; // ── WebSocket ───────────────────────────────────────────────────────────────── /** * Event fired when the WebSocket connection is established. * * @see {@link IWebSocket.onopen} */ export interface IWebSocketOpenEvent { type: 'open'; } /** * Event fired when the WebSocket connection is closed. * * @see {@link IWebSocket.onclose} */ export interface IWebSocketCloseEvent { type: 'close'; /** WebSocket close code per RFC 6455, e.g. `1000` (normal closure). */ code: number; /** Human-readable reason string supplied by the closing peer. */ reason: string; } /** * Event fired when a WebSocket transport error occurs. * * @see {@link IWebSocket.onerror} */ export interface IWebSocketErrorEvent { type: 'error'; /** The underlying error. */ error: Error; } /** * Event fired when a WebSocket ping frame is received. * * @see {@link IWebSocket.onping} */ export interface IWebSocketPingEvent { type: 'ping'; /** Application data carried in the ping frame. */ data: string; } /** * Event fired when a WebSocket pong frame is received. * * @see {@link IWebSocket.onpong} */ export interface IWebSocketPongEvent { type: 'pong'; /** Application data carried in the pong frame. */ data: string; } /** * Event fired when the WebSocket data frame is received. * * @see {@link IWebSocket.onmessage} */ export interface IWebSocketMessageEvent { type: 'message'; /** Payload: `string` for text frames, `ArrayBuffer` for binary frames. */ data: string | ArrayBuffer; } // ── EventSource ─────────────────────────────────────────────────────────────── /** * Event fired when the EventSource connection is established. * * @see {@link IEventSource.onopen} */ export interface IEventSourceOpenEvent { type: 'open'; } /** * Event fired when an SSE message is received. * * @remarks The `type` field reflects the SSE `event:` field value; * defaults to `"message"` when the field is absent. * * @see {@link IEventSource.onmessage} */ export interface IEventSourceMessageEvent { /** Event type; mirrors the SSE `event:` field, defaulting to `"message"`. */ type: string; /** UTF-8 decoded SSE `data:` field value. */ data: string; /** Value of the SSE `id:` field, or an empty string if absent. */ lastEventId: string; } /** * Event fired when the EventSource connection is closed. * * @see {@link IEventSource.onclose} */ export interface IEventSourceCloseEvent { type: 'close'; } /** * Event fired when an EventSource transport error occurs. * * @see {@link IEventSource.onerror} */ export interface IEventSourceErrorEvent { type: 'error'; /** The underlying error. */ error: Error; } /** * A kernel-proxied WebSocket connection returned by {@link IClient.socket}. * * @remarks Unlike the browser `WebSocket`, this object is returned * immediately in a disconnected state. Call {@link IWebSocket.open} to * initiate the connection. All event callbacks are nullable; assign a * function to start receiving events. Every send operation is asynchronous. */ export interface IWebSocket { /** * How binary data is returned in {@link IWebSocket.onmessage}. * * @remarks Always `"arraybuffer"`. */ readonly binaryType: string; /** Number of bytes currently queued for sending but not yet transmitted. */ readonly bufferedAmount: number; /** Negotiated WebSocket extensions, or an empty string if none. */ readonly extensions: string; /** Negotiated sub-protocol, or an empty string if none was negotiated. */ readonly protocol: string; /** Current connection state. See {@link TWebSocketReadyState}. */ readonly readyState: TWebSocketReadyState; /** The WebSocket server URL (e.g. `"ws://127.0.0.1:6806/ws/…"`). */ readonly url: string; /** Called when the connection is established. */ onopen: ((event: IWebSocketOpenEvent) => void | Promise) | null; /** Called when the connection is closed. */ onclose: ((event: IWebSocketCloseEvent) => void | Promise) | null; /** Called when a transport error occurs. */ onerror: ((event: IWebSocketErrorEvent) => void | Promise) | null; /** Called when a ping control frame is received. */ onping: ((event: IWebSocketPingEvent) => void | Promise) | null; /** Called when a pong control frame is received. */ onpong: ((event: IWebSocketPongEvent) => void | Promise) | null; /** Called when a data frame is received. */ onmessage: ((event: IWebSocketMessageEvent) => void | Promise) | null; /** * Initiates the WebSocket connection. * * @remarks The returned `Promise` resolves once the TCP/TLS handshake * succeeds and the HTTP upgrade is confirmed. Calling `open()` more than * once is a no-op — the second call resolves immediately. */ open(): Promise; /** * Sends a text or binary data frame to the remote peer. * * @param data - UTF-8 string for a text frame; `ArrayBuffer` for a binary frame. */ send(data: string | ArrayBuffer): Promise; /** * Sends a ping control frame. * * @param data - Optional application data to include in the frame. */ ping(data?: string): Promise; /** * Sends a pong control frame. * * @param data - Optional application data to include in the frame. */ pong(data?: string): Promise; /** * Initiates a graceful close handshake. * * @param code - WebSocket close code (default `1000` — normal closure). * @param reason - Optional human-readable reason string (max 123 bytes). */ close(code?: number, reason?: string): Promise; } // ── Sub-namespaces ──────────────────────────────────────────────────────────── /** * A kernel-proxied Server-Sent Events connection returned by {@link IClient.event}. * * @remarks The object is returned in {@link TEventSourceReadyState | CONNECTING} state * immediately; the kernel starts the SSE subscription in the background and * fires {@link IEventSource.onopen} once the stream is established. * Call {@link IEventSource.close} to cancel the subscription. */ export interface IEventSource { /** Current connection state. See {@link TEventSourceReadyState}. */ readonly readyState: TEventSourceReadyState; /** The original path passed to {@link IClient.event}, e.g. `"/api/…"`. */ readonly url: string; /** Called when the connection is established. */ onopen: ((event: IEventSourceOpenEvent) => void | Promise) | null; /** Called when a message is received. */ onmessage: ((event: IEventSourceMessageEvent) => void | Promise) | null; /** Called when the connection is closed by the server or after {@link IEventSource.close}. */ onclose: ((event: IEventSourceCloseEvent) => void | Promise) | null; /** Called when a transport error occurs. */ onerror: ((event: IEventSourceErrorEvent) => void | Promise) | null; /** Cancels the subscription and closes the connection. */ close(): void; } /** * Network client utilities exposed as `siyuan.client`. * * @remarks Provides HTTP, WebSocket, and Server-Sent Events access, all * tunnelled through the kernel and authenticated with the plugin token. */ export interface IClient { /** * Tunnels an HTTP request through the kernel's REST API. * * @param path - Absolute path starting with `/`, e.g. `"/api/system/version"`. * @param init - Optional request options (method, headers, body). * @returns A {@link IFetchResponse} with lazy body accessor methods. */ fetch(path: TRequestPath, init?: IRequestInit): Promise; /** * Creates a WebSocket connection proxied through the kernel. * * @remarks The returned object is in {@link TWebSocketReadyState | CONNECTING} * state but not yet connected. Call {@link IWebSocket.open} to initiate * the handshake. * * @param path - Absolute path starting with `/`. * @param protocols - Optional WebSocket sub-protocol(s) to negotiate. * @returns A sealed {@link IWebSocket} handle. */ socket(path: TRequestPath, protocols?: string | string[]): Promise; /** * Opens a Server-Sent Events stream proxied through the kernel. * * @param path - Absolute path starting with `/`. * @returns A sealed {@link IEventSource} handle. */ event(path: TRequestPath): Promise; } // ── Plugin sub-namespaces ───────────────────────────────────────────────────── /** * Static metadata for the running plugin instance. * * @remarks Exposed as `siyuan.plugin`. All properties are read-only at * runtime; the values are set by the kernel before `onload` is called. */ export interface IPlugin { /** Internal plugin identifier (matches the plugin directory name). */ readonly name: string; /** Semantic version string, e.g. `"1.0.0"`. */ readonly version: string; /** Human-readable display name shown in the plugin marketplace. */ readonly displayName: string; /** Backend platform identifier, e.g. `"windows"`, `"linux"`, `"darwin"`. */ readonly platform: string; /** Localization strings loaded from the plugin's `i18n/` directory. */ readonly i18n: Record; /** Kernel lifecycle hooks for this plugin. */ readonly lifecycle: IPluginLifecycle; } /** * Optional lifecycle callbacks invoked by the kernel at state transitions. * * @remarks Exposed as `siyuan.plugin.lifecycle`. Assign a function to any * property to subscribe; the kernel awaits any returned `Promise` before * advancing to the next lifecycle stage. Unset callbacks (`null`) are skipped. */ export interface IPluginLifecycle { /** Called when the plugin script is first evaluated (before the `running` state.). */ onload: (() => void | Promise) | null; /** Called when the plugin transitions to the `running` state. */ onrunning: (() => void | Promise) | null; /** Called when the plugin is being unloaded (e.g. on shutdown or hot-reload). */ onunload: (() => void | Promise) | null; } /** * Kernel event bridge. * * @remarks Exposed as `siyuan.event`. Allows the plugin to receive kernel * broadcast events and publish events to the in-process bus. */ export interface IEvent { /** * Inbound kernel event handler. * * @remarks Assign a function to receive every kernel dispatched event. * Set to `null` to stop receiving events. */ handler: ((event: TEventMessage) => void | Promise) | null; /** * Publishes an event to the in-process event bus. * * @param topic - Event topic string used to route the event to subscribers. * @param event - Arbitrary serializable payload. */ emit(topic: string, event: IEventMessage): Promise; } /** * Structured logger for the plugin. * * @remarks Exposed as `siyuan.logger`. Level semantics mirror the browser * `console` API (`trace` < `debug` < `info` < `warn` < `error`). Output is * written to the kernel log file and prefixed with the plugin name. */ export interface ILogger { /** Emits a `TRACE`-level log entry. */ readonly trace: (...args: any[]) => Promise; /** Emits a `DEBUG`-level log entry. */ readonly debug: (...args: any[]) => Promise; /** Emits an `INFO`-level log entry. */ readonly info: (...args: any[]) => Promise; /** Emits a `WARN`-level log entry. */ readonly warn: (...args: any[]) => Promise; /** Emits an `ERROR`-level log entry. */ readonly error: (...args: any[]) => Promise; } /** * Scoped file storage for the plugin. * * @remarks Exposed as `siyuan.storage`. All paths are relative to the * plugin's data directory at `data/plugins//`. Forward slashes are * accepted on all platforms. */ export interface IStorage { /** * Reads a file and returns a lazy data accessor. * * @param path - Path relative to the plugin data directory. * @returns A {@link IDataObject} wrapping the file contents. * @throws Rejects if the file does not exist. */ get(path: string): Promise; /** * Creates or overwrites a file with the provided UTF-8 string content. * * @param path - Path relative to the plugin data directory. * @param content - UTF-8 encoded content to write. */ put(path: string, content: string): Promise; /** * Deletes a file or recursively removes a directory tree. * * @param path - Path relative to the plugin data directory. */ remove(path: string): Promise; /** * Lists the entries in a directory. * * @param path - Path relative to the plugin data directory. * @returns An array of {@link IStorageEntry} descriptors. */ list(path: string): Promise; readonly watcher: IStorageWatcher; } /** * Controls which storage paths the plugin's file-system watcher monitors. * Changes on watched paths are delivered as {@link IFsNotifyEventMessage} * events on the runtime event bus. */ export interface IStorageWatcher { /** * Resolves `path` and registers it with the file-system watcher. * @param path - Path relative to the storage directory to start watching. */ add(path: string): Promise; /** * Resolves `path` and unregisters it from the file-system watcher. * @param path - Path relative to the storage directory to stop watching. */ remove(path: string): Promise; } /** RPC method handler type. */ export type THandler = (...args: any[]) => any | Promise; /** Agent capability handler type. */ export type TAgentCapabilityHandler = (input: Record) => any | Promise; /** * JSON-RPC method registry for the plugin. * * @remarks Exposed as `siyuan.rpc`. Registered methods are callable by * external clients via `GET /api/plugin/rpc`, `POST /api/plugin/rpc`, or * the WebSocket endpoint `GET /ws/plugin/rpc`. */ export interface IRpc { /** * Registers a named RPC method callable by external clients. * * @param name - Unique method name used to dispatch the call. * @param handler - Handler function; may be async. * @param descriptions - Optional human-readable description strings. */ bind( name: string, handler: THandler, ...descriptions: string[] ): Promise; /** * Unregisters a previously registered RPC method. * * @param name - The method name originally passed to {@link IRpc.bind}. */ unbind(name: string): Promise; /** * Broadcasts a JSON-RPC notification to all connected clients. * * @param method - Notification method name. * @param params - Optional notification parameters. */ broadcast(method: string, params?: any[] | Record): Promise; } /** Agent capability registry exposed to plugins as `siyuan.agent`. */ export interface IAgent { /** * Registers an Agent capability. * * The capability name is automatically namespaced and suffixed with a stable hash * to avoid collisions between plugins. * * @param name - The capability name local to this plugin (e.g. `"my-capability"`). * @param config - Metadata, schemas, and declared side effects for the capability. * @param handler - The function invoked when an Agent calls the capability. * @returns The registration record, including the fully-qualified tool name. */ registerCapability( name: string, config: IAgentCapabilityConfig, handler: TAgentCapabilityHandler, ): Promise; /** * Unregisters a previously registered Agent capability. * * Uses the same local name passed to {@link registerCapability}; the kernel resolves * the fully-qualified name internally. * * @param name - The local capability name used when the capability was registered. */ unregisterCapability(name: string): Promise; } /** Side effects declared by an Agent capability or one of its actions. */ export interface IAgentCapabilityEffects { /** Reads local data. */ localRead?: boolean; /** Modifies local data. */ localWrite?: boolean; /** Sends data outside the local environment. */ dataEgress?: boolean; /** May incur an external cost. */ externalCost?: boolean; } /** Metadata, schemas, and side effects describing an Agent capability. */ export interface IAgentCapabilityConfig { /** Human-readable display name shown in Agent UIs. */ title?: string; /** Natural-language description used by the Agent to discover and select the capability. */ description: string; /** JSON Schema describing the capability's input parameters. */ inputSchema: JSONSchema.ObjectSchema; /** JSON Schema describing the capability's output. */ outputSchema?: JSONSchema.Schema; /** Default side effects for the capability. */ effects?: IAgentCapabilityEffects; /** Side effects for individual values of the input `action` property. */ actionEffects?: Record; } /** * The registration record returned by {@link IAgent.registerCapability}. */ export interface IRegisteredCapability extends IAgentCapabilityConfig { /** Stable capability identifier used by Agent configuration. */ id: string; /** * The fully-qualified tool name exposed to the Agent. * * @example "plugin__plugin_name__capability_name__0123456789ab" */ name: string; } // ── Server request types ───────────────────────────────────────────────────── /** * Serialization format for a structured {@link IResponseBody.data} payload. * * @remarks The kernel delegates to the corresponding Gin writer: * JSON variants map to `c.JSON` / `c.JSONP` / `c.AsciiJSON` / etc.; * `XML` → `c.XML`; `YAML` → `c.YAML`; `TOML` → `c.TOML`; * `ProtoBuf` → `c.ProtoBuf`. */ export type TSerializedType = | 'JSON' | 'JSONP' | 'AsciiJSON' | 'IndentedJSON' | 'PureJSON' | 'SecureJSON' | 'XML' | 'YAML' | 'TOML' | 'ProtoBuf'; /** * HTTP Basic authentication credentials extracted from the request URL. */ export interface IRequestUser { /** Decoded username. */ username: string; /** Decoded password. */ password: string; } /** * Parsed URL components of an incoming server request. * * @remarks Field names mirror the browser `URL` / `Location` API where * applicable (`pathname`, `hash`, `search`). */ export interface IRequestUrl { /** Basic-auth credentials, or `null` if the request carries none. */ user: IRequestUser | null; /** Value of the `Host` request header, e.g. `"127.0.0.1:6806"`. */ host: string; /** URL-decoded path, e.g. `"/plugin/private/sample/api/hello/a space"`. */ path: string; /** Percent-encoded path, e.g. `"/plugin/private/sample/api/hello/a%20space"`. */ pathname: string; /** URL-decoded fragment without the leading `#`. */ fragment: string; /** Percent-encoded fragment without the leading `#`. */ hash: string; /** Raw query string without the leading `?`, e.g. `"a=1&b=2"`. */ search: string; /** Parsed query parameters, e.g. `{ a: ["1"], b: ["2"] }`. */ query: Record; } /** * An uploaded file part within a `multipart/form-data` request. */ export interface IRequestFile { /** Original filename provided by the client. */ filename: string; /** MIME part headers (e.g. `Content-Disposition`, `Content-Type`). */ headers: Record; /** File size in bytes. */ size: number; /** * File contents as a lazy {@link IDataObject}. * * @remarks `null` if the file could not be read during request parsing. */ data: IDataObject | null; } /** * Parsed form data from an `application/x-www-form-urlencoded` or * `multipart/form-data` request. */ export interface IRequestForm { /** * String form fields keyed by field name. * * @remarks Each key maps to an array to support repeated fields with the * same name, e.g. `{ tags: ["a", "b"] }`. */ values: Record; /** Uploaded file parts keyed by field name. */ files: Record; } /** * Body of an incoming server request. * * @remarks Exactly one of `form` or `data` is non-null: * `form` is set for `application/x-www-form-urlencoded` and * `multipart/form-data`; `data` is set for all other content types and is * `null` when the request carries no body. */ export interface IRequestBody { /** * Parsed form data. * * @remarks `null` for non-form requests. */ form: IRequestForm | null; /** * Raw request body as a lazy {@link IDataObject}. * * @remarks `null` when `form` is non-null or the request carries no body. */ data: IDataObject | null; } /** * HTTP request-line and header fields. * * @remarks The `Cookie` and `Authorization` headers are stripped from * `headers` before the request is forwarded to the plugin handler. */ export interface IRequestContent { /** HTTP method in upper-case, e.g. `"GET"`, `"POST"`. */ method: string; /** Full request URI including the query string, e.g. `"/plugin/private/sample/api/hello?a=1"`. */ uri: string; /** HTTP protocol version string, e.g. `"HTTP/1.1"`. */ proto: string; /** Major protocol version number, e.g. `1`. */ protoMajor: number; /** Minor protocol version number, e.g. `1`. */ protoMinor: number; /** * Request headers with `Cookie` and `Authorization` redacted. * * @remarks Each header name maps to an array of values to handle * repeated headers, e.g. `{ "Accept-Encoding": ["gzip", "br"] }`. */ headers: Record; /** * Request cookies keyed by cookie name. * * @remarks Each name maps to an array to handle duplicate cookie names. */ cookies: Record; /** Media type from the `Content-Type` header (parameters stripped), e.g. `"application/json"`. */ contentType: string; /** Value of the `Content-Length` header in bytes; `-1` if unknown. */ contentLength: number; /** Value of the `Referer` header, or an empty string if absent. */ referer: string; /** Value of the `User-Agent` header. */ userAgent: string; /** Parsed request body. */ body: IRequestBody; } /** * Gin routing context for an incoming server request. */ export interface IRequestContext { /** * The sub-path captured by the `*path` wildcard parameter. * * @example `"/api/hello"` for a request to `/plugin/private/sample/api/hello`. */ path: string; /** Full Gin route template, e.g. `"/plugin/private/:name/*path"`. */ fullPath: string; /** Best-guess client IP address (honors `X-Forwarded-For` / `X-Real-IP`). */ clientIp: string; /** Remote IP of the TCP connection (proxy headers are not considered). */ remoteIp: string; /** `host:port` of the remote TCP endpoint, e.g. `"127.0.0.1:54321"`. */ remoteAddr: string; /** * Named route parameters extracted by Gin. * * @example `{ name: ["plugin-sample"], path: ["/api/hello"] }` */ params: Record; } /** * The complete request object passed as the sole argument to server handlers. */ export interface IServerRequest { /** Parsed URL components. */ url: IRequestUrl; /** HTTP request-line, headers, and body. */ request: IRequestContent; /** Gin routing context. */ context: IRequestContext; } // ── Server response types ───────────────────────────────────────────────────── /** * A structured-data response body serialized by the kernel. * * @remarks The kernel selects the Gin writer that corresponds to `type` * (e.g. `c.JSON` for `"JSON"`, `c.XML` for `"XML"`). */ export interface IResponseSerializedData { /** Serialization format to use. */ type: TSerializedType; /** The value to serialize; must be compatible with the chosen format. */ data: any; } /** * A file response body served directly from the local filesystem. * * @remarks When `name` is non-empty the kernel sends the file as a * downloadable attachment (`Content-Disposition: attachment; filename=""`). * When `name` is empty or omitted the file is served inline via `c.File`. */ export interface IResponseFile { /** * Download filename for the `Content-Disposition` header. * * @remarks Omit or leave empty to serve the file inline. */ name?: string; /** Absolute filesystem path to the file on the server host. */ path: string; } /** * A formatted-string response body. * * @remarks The kernel passes `format` and `values` to Go's `fmt.Sprintf` * and writes the resulting string via `c.String`. */ export interface IResponseString { /** Go `fmt.Sprintf`-style format string, e.g. `"Hello, %s!"`. */ format: string; /** Positional arguments interpolated into `format`. */ values?: any[]; } /** * A raw-bytes response body with an explicit `Content-Type`. * * @remarks Written to the response via `c.Data`. `data` accepts a UTF-8 * string, a Node.js `Buffer`, or an `ArrayBuffer`; the kernel converts all * three forms to `[]byte` before writing. */ export interface IResponseRawData { /** MIME type for the `Content-Type` response header, e.g. `"image/png"`. */ contentType: string; /** Raw response body bytes. */ data: string | ArrayBuffer; } /** * A redirect response body. * * @remarks The kernel issues the redirect via `c.Redirect` using the * `statusCode` from the enclosing {@link IHttpResponse}. */ export interface IResponseRedirect { /** Target URL; may be absolute or relative. */ location: string; } /** * The body of an HTTP response returned by a server handler. * * @remarks Set exactly one field; the kernel inspects `data`, `file`, * `string`, `raw`, and `redirect` in that order and uses the first * non-null value. Returning an empty object (all fields absent or null) * results in a status-only response via `c.Status`. */ export interface IResponseBody { /** Structured data serialized by the kernel (JSON, XML, YAML, …). */ data?: IResponseSerializedData | null; /** File served from the local filesystem. */ file?: IResponseFile | null; /** Formatted string written via `fmt.Sprintf`. */ string?: IResponseString | null; /** Raw bytes with an explicit `Content-Type`. */ raw?: IResponseRawData | null; /** HTTP redirect. */ redirect?: IResponseRedirect | null; } /** * A `Set-Cookie` descriptor included in an {@link IHttpResponse}. * * @remarks Field names use PascalCase because they mirror Go's * `net/http.Cookie` struct, which has no JSON tags and therefore serializes * its exported field names verbatim. */ export interface IResponseCookie { /** Cookie name. */ Name: string; /** Cookie value. */ Value: string; /** `true` if the value should be wrapped in double-quotes in the header. */ Quoted?: boolean; /** Cookie path scope, e.g. `"/plugin/private/my-plugin/"`. */ Path?: string; /** Cookie domain scope. */ Domain?: string; /** Absolute expiry time as an ISO 8601 string. */ Expires?: string; /** Raw, unparsed `Expires` attribute string (informational). */ RawExpires?: string; /** `Max-Age` in seconds. `0` deletes the cookie; negative values are not sent. */ MaxAge?: number; /** Restricts the cookie to HTTPS connections. */ Secure?: boolean; /** Hides the cookie from JavaScript (`HttpOnly` flag). */ HttpOnly?: boolean; /** * `SameSite` cookie policy. * * @remarks Maps to Go `http.SameSite` constants: * `0` = default (browser-defined), `1` = None, `2` = Lax, `3` = Strict. */ SameSite?: number; /** Sets the `Partitioned` (CHIPS) cookie attribute. */ Partitioned?: boolean; /** Raw `Set-Cookie` line as sent by the server (informational). */ Raw?: string; /** Unparsed attribute strings not recognized by the Go cookie parser. */ Unparsed?: string[] | null; } /** * The return value expected from an HTTP server handler. */ export interface IHttpResponse { /** HTTP status code to send, e.g. `200`, `404`. */ statusCode: number; /** * Additional response headers. * * @remarks Each header name maps to an array of values to support * multi-value headers such as `Link` or repeated `Set-Cookie` entries. */ headers?: Record; /** Cookies to attach to the response via `Set-Cookie` headers. */ cookies?: IResponseCookie[]; /** Response body. Omit or set to `null` for a header-only response. */ body?: IResponseBody | null; } // ── Server handler interfaces ───────────────────────────────────────────────── /** * A single Server-Sent Event (SSE) frame sent from the server to the client. * * Each field corresponds to a line prefix defined by the SSE specification * {@link https://html.spec.whatwg.org/multipage/server-sent-events.html | HTML Standard - 9.2 Server-sent events}. */ export interface IServerSentEvent { /** `id:` field — sets the event source's last-event-ID, used for reconnection replay. */ id?: string; /** `event:` field — custom event type. */ event?: string; /** `data:` field — the payload of the event. Multi-line values are joined with `\n`. */ data: any; /** `retry:` field — overrides the client's reconnection delay (milliseconds). */ retry?: number; } /** * Server-side SSE (Server-Sent Events) port provided to * {@link IServerEventSourceRequest.port}. * * @remarks * The kernel opens the SSE response stream before invoking the handler. * Once streaming begins, {@link IEventSourcePort.onopen} fires to signal that * the stream is ready for events. Call {@link IEventSourcePort.send} to push * SSE events to the client and {@link IEventSourcePort.close} to terminate * the stream. The connection stays open until `close()` is called or the * client disconnects. */ export interface IEventSourcePort { /** Called once when the SSE stream is ready to accept events. */ onopen: ((event: IEventSourceOpenEvent) => void | Promise) | null; /** Called when the client disconnects or after {@link IEventSourcePort.close}. */ onclose: ((event: IEventSourceCloseEvent) => void | Promise) | null; /** * Pushes one SSE event to the connected client. * * @remarks * `send` is synchronous — no `await` required. It enqueues the event in * the kernel's SSE write buffer; the actual flush is asynchronous. * * @param event - The SSE event to send. */ send(event: IServerSentEvent): void; /** Terminates the SSE stream and closes the response. */ close(): void; } /** * The request object received by {@link IServerScope.ws | WebSocket server handlers}. * * @remarks * Extends {@link IServerRequest} with a `port` property that mirrors the * {@link IWebSocket} client interface. The kernel upgrades the HTTP connection * to WebSocket before invoking the handler. After the handler returns, the * kernel auto-opens the port's read loop if `port.open()` was not called * explicitly. */ export interface IServerWebSocketRequest extends IServerRequest { /** * Bidirectional WebSocket port connected to the client. * * @remarks * Assign event callbacks (`onopen`, `onmessage`, `onping`, `onpong`, * `onclose`, `onerror`) before the handler returns. Calling `port.open()` * is optional — the kernel opens the read loop automatically. */ readonly port: Omit; } /** * The request object received by {@link IServerScope.es | SSE server handlers}. * * @remarks * Extends {@link IServerRequest} with a `port` property for pushing * Server-Sent Events to the client. The kernel opens the SSE response before * the handler is invoked; {@link IEventSourcePort.onopen} fires once streaming * begins. */ export interface IServerEventSourceRequest extends IServerRequest { /** * Server-side SSE port for pushing events to the connected client. * * @remarks * Assign `onopen` and `onclose` callbacks in the handler body. Call * `port.send(eventType, data)` inside `onopen` to emit SSE events. */ readonly port: IEventSourcePort; } /** * Handler slot for one request type within a server scope. * * @remarks * The object is sealed by the kernel; only the `handler` property may be * reassigned. Set `handler` to `null` to leave the slot empty — the kernel * will return `500 Internal Server Error` for any unhandled request. * * @typeParam TRes - Expected return type of the handler function. * @typeParam TReq - Request object type passed to the handler. Defaults to * {@link IServerRequest} for the HTTP slot; specialised to * {@link IServerWebSocketRequest} and {@link IServerEventSourceRequest} for the WS and SSE * slots respectively. */ export interface IServerRequestHandler { /** * The function invoked for each incoming request of this type. * * @remarks * The kernel passes the parsed request as the sole argument and awaits * any returned `Promise` before writing the response. */ handler: ((request: TReq) => TRes | Promise) | null; } /** * All request-type handler slots for one access scope. * * @remarks The object is frozen by the kernel; only the `handler` property * on each child object may be reassigned. */ export interface IServerScope { /** * HTTP request handler. * * @remarks Handles all HTTP methods at `ANY /plugin/private//*path`. * The handler must return an {@link IHttpResponse}. */ readonly http: IServerRequestHandler; /** * WebSocket upgrade handler. * * @remarks * The handler receives an {@link IServerWebSocketRequest} that includes * `request.port`, a bidirectional {@link IWebSocket} connected to the * client. Assign event callbacks before the handler returns; the kernel * auto-opens the port's read loop afterwards. */ readonly ws: IServerRequestHandler; /** * Server-Sent Events handler. * * @remarks * The handler receives an {@link IServerEventSourceRequest} that includes * `request.port`, an {@link IEventSourcePort} for pushing SSE events. * Assign `onopen` / `onclose` callbacks and call `port.send` inside * `onopen`. */ readonly es: IServerRequestHandler; } /** * Web server handler registry for the plugin. * * @remarks Exposed as `siyuan.server`. The kernel creates one frozen scope * object per access level. Only the `handler` properties inside each scope * object are writable. */ export interface IServer { /** * Private-scope handler group. * * @remarks Routes under `/plugin/private//*path` require kernel * authentication and admin role before the request reaches the handler. * The `` segment must match the running plugin's `name`. */ readonly private: IServerScope; } // ── Top-level interface ─────────────────────────────────────────────────────── /** * The root `siyuan` global exposed to every kernel plugin script. * * @remarks Available as the global constant `siyuan`. All async operations * return `Promise`s resolved on the plugin's JavaScript runtime event loop. */ export interface ISiyuan { /** Static metadata about this plugin instance. */ readonly plugin: IPlugin; /** Kernel event bridge. */ readonly event: IEvent; /** Structured logger. */ readonly logger: ILogger; /** Scoped persistent file storage. */ readonly storage: IStorage; /** JSON-RPC method registry. */ readonly rpc: IRpc; /** Agent capability registry. */ readonly agent: IAgent; /** Network client utilities (HTTP, WebSocket, SSE). */ readonly client: IClient; /** Web request handler registry. */ readonly server: IServer; }