On This Page

Client and Lifecycle

29 symbols from @neontechspace/fluxerly

Functions#

createClient#

Create a bot client from a token, initially Disconnected

createClient<
    F extends MessageFields | undefined = undefined
>(options: ClientOptions<F>): Client<SelectedMessage<F>>createClient<
  F extends
    | MessageFields | undefined
    = undefined
>(
  options: ClientOptions<F>
): Client<SelectedMessage<F>>

Use run or connect to receive gateway events. HTTP requests work without connecting. Always finish with shutdown unless an accepted run already manages the client's full lifetime

Parameters

options
ClientOptions<F>

Creation and configuration

Creation validates configuration synchronously without authenticating the token or opening sockets. No timers or process signal handlers are started. Invalid options throw ConfigurationError without the rejected value. An unsupported option key, such as a misspelled logging, throws with a hint naming the closest supported key. A missing or blank token, such as an unset environment variable, throws ConfigurationError with a hint naming that likely cause. Unexpected creation failures throw SdkDefect. An option getter that throws produces code application.defect with the thrown value as the cause

Instance and caching

Hosted Fluxer is selected by default. For a self-hosted instance, pass its root and let the SDK discover API, gateway and URL endpoints when first needed. HTTPS and WSS are required unless that explicit instance enables allowInsecure for HTTP and WS

Caching is disabled by default. Cache settings are copied and validated without calling retention policies or reporters. Unknown cache or message-cache keys fail validation. The messageFields option selects received message fields once for this client's lifetime

Connection and sharding

Gateway startup defaults to a 30,000 ms overall budget and three total attempts per assigned shard. An explicit sharding plan fixes this client's assigned IDs for its lifetime, while automatic sharding and a client without sharding settings may move to a larger plan when Fluxer asks for more shards. Shard zero receives direct-message gateway traffic. Request and cache limits still apply across the whole client, not separately to each shard

import { createClient } from "@neontechspace/fluxerly"

export function shardingExample(token) {
    return createClient({ token, sharding: { totalShards: 4, shardIds: [0, 2] } })
}
import { createClient } from "@neontechspace/fluxerly"

export function shardingExample(token: string) {
    return createClient({ token, sharding: { totalShards: 4, shardIds: [0, 2] } })
}

defineBot#

Define a bot's runBot options without its token, so a module can export them for both runBot and createTestBot from the testing entry point

Returns the same object unchanged, without copying, reading or validating it. runBot and createTestBot check the options when they are called, as they do for an inline object. The function exists for type inference: Command arguments keep their declared types, such as a text argument's value as a string, which an object annotated with BotOptions loses. TypeScript rejects a token here, so the program passes it to runBot, usually from an environment variable

defineBot<
    F extends MessageFields | undefined = undefined,
    S extends Readonly<Record<string, unknown>>
        = Readonly<
            Record<
                string,
                | Readonly<
                      Record<string, CommandArgumentDescriptor>
                  >
                | undefined
            >
        >
>(
    options: Omit<BotOptions<F, S>, "token">
): Omit<BotOptions<F, S>, "token">defineBot<
  F extends
    | MessageFields | undefined
    = undefined,
  S extends Readonly<
    Record<string, unknown>
  >
    = Readonly<
      Record<
        string,
        | Readonly<
            Record<
              string,
              CommandArgumentDescriptor
            >
          >
        | undefined
      >
    >
>(
  options:
    Omit<BotOptions<F, S>, "token">
): Omit<BotOptions<F, S>, "token">

Parameters

options
Omit<BotOptions<F, S>, "token">
Returns
Omit<BotOptions<F, S>, "token">
import { defineBot, runBot } from "@neontechspace/fluxerly"

// A test imports bot and passes it to createTestBot from @neontechspace/fluxerly/testing
export const bot = defineBot({
    commands: {
        prefix: "!",
        commands: {
            greet: {
                arguments: { name: { type: "text", rest: true } },
                execute: ({ reply, values }) => reply(`Hello, ${values.name}!`),
            },
        },
    },
})

// Running this file starts the bot, while importing it from a test does not
if (import.meta.main)
    await runBot({ token: process.env.FLUXER_BOT_TOKEN, ...bot })
import { defineBot, runBot } from "@neontechspace/fluxerly"

// A test imports bot and passes it to createTestBot from @neontechspace/fluxerly/testing
export const bot = defineBot({
    commands: {
        prefix: "!",
        commands: {
            greet: {
                arguments: { name: { type: "text", rest: true } },
                execute: ({ reply, values }) => reply(`Hello, ${values.name}!`),
            },
        },
    },
})

// Running this file starts the bot, while importing it from a test does not
if (import.meta.main) await runBot({ token: process.env.FLUXER_BOT_TOKEN, ...bot })

runBot#

Start a bot from event handlers and prefix commands, and watch its connection and subscriptions

The client is created and every handler and command is registered synchronously, before this function returns and before the gateway connects, so no event is missed and no handler runs before registration and setup complete. Invalid options, such as a misspelled option key, an unknown event name, invalid delivery settings or an invalid command, throw ConfigurationError at once. Every option is checked before any client, signal listener or request exists, even when the signal is already aborted, so misuse leaves nothing to clean up. A missing or empty token, usually from an unset environment variable, is reported like a failed run instead: Once every other option is valid, runBot returns an Err ConfigurationError with field token before any client exists. The returned ResultAsync then runs the bot until it stops. Handler contexts expose the full client. Message-create contexts also expose message and a cancellation-aware reply. Handler and command failures are reported to onError, or logged in full, without restarting them or stopping the bot. By default, SIGINT and SIGTERM request a normal stop and messages written by bots skip the events handlers and prefix commands. Set processSignals or ignoreBots to false to opt out. Stopping waits for SDK cleanup, not unrelated application Promises. Expected runner failures return Err, including ApplicationError when the setup callback or a commands register callback fails and ConfigurationError for a missing token, and SDK defects reject with SdkDefect. By default a failed run is also logged once, with code lifecycle.botFailed unless the client already logged that error, and sets process.exitCode to 1, so await runBot({...}) needs no further handling. Set reportFailure to false when the application handles the returned failure and exit status itself. A missing token or a commands-registration failure reported before client creation uses the configured logging settings, prints no stack for the missing token and masks the token after removing surrounding whitespace and one pair of matching quotes. Unusable logging settings are misuse. An option getter that throws while the options are checked throws SdkDefect with code application.defect and the thrown value as the cause, before any client exists. Other misuse and option-getter failures throw synchronously without a runner report or exit-status change, regardless of reportFailure

runBot<
    F extends MessageFields | undefined = undefined,
    S extends Readonly<Record<string, unknown>>
        = Readonly<
            Record<
                string,
                | Readonly<
                      Record<string, CommandArgumentDescriptor>
                  >
                | undefined
            >
        >
>(options: BotOptions<F, S>): ResultAsync<
    void,
    | ConfigurationError | CriticalWorkerStoppedError
    | CancelledError | ApplicationError | ConnectError
>runBot<
  F extends
    | MessageFields | undefined
    = undefined,
  S extends Readonly<
    Record<string, unknown>
  >
    = Readonly<
      Record<
        string,
        | Readonly<
            Record<
              string,
              CommandArgumentDescriptor
            >
          >
        | undefined
      >
    >
>(
  options: BotOptions<F, S>
): ResultAsync<
  void,
  | ConfigurationError
  | CriticalWorkerStoppedError
  | CancelledError
  | ApplicationError | ConnectError
>

Parameters

options
BotOptions<F, S>

Aborting the optional signal requests a normal stop, not a cancellation Err. A requested stop first stops accepting events and lets running handlers, scheduled tasks and their requests finish for up to drainMs, default 5,000 ms. Success means the client has stopped and cleanup has finished. The runner always shuts down its client, including after a failure. If an event or command subscription closes while the bot is still running, the result fails with CriticalWorkerStoppedError. That includes a handler or command router set to overflow "stop" whose queue overflows. The overflow is still reported to onError or logged, and the error names the event and the exceeded capacity, with the EventOverflowError as its cause. Process signal listeners are removed when the run finishes, and the runner never exits the process

import { runBot } from "@neontechspace/fluxerly"

export function pingBot(token) {
    return runBot({
        token,
        commands: {
            prefix: "!",
            commands: {
                ping: { execute: ({ reply }) => reply("Pong!") },
                roll: {
                    arguments: { sides: { type: "integer", min: 2, max: 100, default: 6 } },
                    execute: ({ reply, values }) => reply(`${1 + Math.floor(Math.random() * values.sides)}`),
                },
            },
        },
    })
}
import { runBot } from "@neontechspace/fluxerly"

export function pingBot(token: string | undefined) {
    return runBot({
        token,
        commands: {
            prefix: "!",
            commands: {
                ping: { execute: ({ reply }) => reply("Pong!") },
                roll: {
                    arguments: { sides: { type: "integer", min: 2, max: 100, default: 6 } },
                    execute: ({ reply, values }) => reply(`${1 + Math.floor(Math.random() * values.sides)}`),
                },
            },
        },
    })
}

Interfaces#

BotApplication#

Application details returned by application.fetch for this client's bot token

Use id to build an installation link. The bot flags show the current installation settings, and ownerId identifies the account that owns the application. The SDK freezes this result but does not cache it. Of the owner it keeps only the user ID, and it contains no redirect URIs, verification keys, client secrets or nested bot account

interface BotApplication
botPublic#

Whether Fluxer currently permits non-owners to install this bot

boolean
botRequireCodeGrant#

Whether Fluxer currently requires its OAuth2 code-grant flow for this bot

boolean
description#

Provider's application description, sourced from the bot bio and null when absent

string | null
icon#

Provider's application icon hash, null when the bot has no avatar

string | null
id#

Decimal application ID, suitable for links.installation

string
name#

Provider application name

string
ownerId#

Decimal user ID of the account that owns the application, the ID that guards.ownerOnly() allows when called without IDs

string

BotEventOptions#

A runBot event handler with its own delivery settings

Omitted settings use the runBot defaults: Overflow dropOldest, concurrency 8 for messageCreate or a partitioned handler and 1 otherwise, and the client.on queue limits

interface BotEventOptions<
    K extends EventName,
    M extends MessageCore = Message
>
    extends HandlerOptions<EventMap<M>[K]>interface BotEventOptions<
  K extends EventName,
  M extends MessageCore = Message
>
  extends HandlerOptions<
    EventMap<M>[K]
  >
handler#

Called for each event

BotEventHandler<K, M>
onError optional#

Receive this handler's failures instead of the client-level onError

(report: FailureReport) => unknown
partitionReady optional#

Hold the events of a partition key until its own setup is done, as the client.on option partitionReady does

Valid only together with partition, otherwise runBot throws ConfigurationError before any client exists

(key: string, signal: AbortSignal) => unknown(key: string, signal: AbortSignal)
  => unknown

Inherited from HandlerOptions: concurrency, maxPendingBytes, maxPendingMessages, overflow, partition

BotOptions#

Configure a bot: Client settings, event handlers, prefix commands, optional startup work and stop signals

A missing token or a commands-registration failure reported before client creation uses the configured logging settings and masks the normalized token, including a token enclosed in matching quotes. Other misuse and option-getter failures instead throw synchronously without a runner report or exit-status change

interface BotOptions<
    F extends MessageFields | undefined = undefined,
    S extends Readonly<Record<string, unknown>>
        = Readonly<
            Record<string, CommandArgumentSchema | undefined>
        >
>
    extends ClientOptions<F>, RunBotOptionsinterface BotOptions<
  F extends
    | MessageFields | undefined
    = undefined,
  S extends Readonly<
    Record<string, unknown>
  >
    = Readonly<
      Record<
        string,
        | CommandArgumentSchema
        | undefined
      >
    >
>
  extends ClientOptions<
    F
  >, RunBotOptions
commands optional#

Prefix commands registered and attached before connecting

See BotCommandsOptions

BotCommandsOptions<SelectedMessage<F>, S>BotCommandsOptions<
  SelectedMessage<F>, S
>
events optional#

Handlers registered before connecting and closed when the bot stops

Own enumerable entries are read once when runBot is called. Undefined handlers are skipped. Pass a function, or an object with a handler and its own delivery settings. By default messageCreate and partitioned handlers run up to 8 callbacks at a time and other events 1. When a queue fills, the oldest waiting event is dropped with a Warn record, and the bot keeps running. With a partition, the overflow option's rule decides which event is dropped. Bot-authored messageCreate and messageUpdate events skip these handlers unless ignoreBots is false

BotEvents<SelectedMessage<F>>
ignoreBots optional#

Skip messageCreate and messageUpdate events whose author is a bot, including this bot's own messages, before they reach the events handlers or prefix commands

Defaults to true, so a reply cannot trigger its own handler. Set false for a bridge or logging bot that handles every message, and check message.author.isBot where needed. Prefix commands follow the same setting unless commands.ignoreBots sets its own. A value other than a boolean throws ConfigurationError

boolean
setup optional#

Startup work after events and commands are registered and before the gateway connects, such as extra subscriptions, cache warm-up or scheduled tasks

The bot connects only after a returned promise settles, so no handler runs before setup finishes. Start timers and background work with client.schedule, whose tasks belong to the bot: A requested stop lets their running work finish within drainMs, every stop cancels the rest, and a failure reaches onError or the log. The signal aborts when the bot begins stopping for any reason, including a requested stop before its drain, a failure and a stop while setup is still running. Pass it to SDK operations, or listen for its abort event to end other work started here, which the runner does not track or await. A throw, rejection or returned or resolved Err result stops the bot before connecting, and runBot returns an Err ApplicationError whose source is "runBot setup" and whose cause is the original value, such as the GuildOperationError of a failed read. A thrown SdkDefect remains a defect and rejects with SdkDefect. The Effect API runs setup in the bot's Scope instead, so its finalizers and scoped fibers end with the bot

(
    client: Client<SelectedMessage<F>>,
    context: { readonly signal: AbortSignal }
) => unknown(
  client: Client<SelectedMessage<F>>,
  context: {
    readonly signal: AbortSignal
  }
) => unknown

Inherited from ClientOptions: cache, connection, gateway, instance, logging, messageFields, observe, onError, rest, sharding, token, transport, uploads

Inherited from RunBotOptions: drainMs, processSignals, reportFailure, signal

Client
Use a bot client for messages, community resources, HTTP requests and gateway events

CurrentBotApplication#

Read the authenticated bot's application identity, its owner's user ID and selected public settings

No gateway connection, application management or authorization-page navigation is performed. The GET uses shared request limits, the client's default deadline (rest.defaultTimeoutMs, 30,000 ms unless configured) and at most two transient read retries. The frozen result is not cached. Of the owner only the user ID is kept, and redirect URIs, verification keys, client secrets and nested bot fields are excluded. Fluxer decides application visibility and installability. Input, HTTP and malformed-response failures return BotApplicationOperationError. Closure returns ClientClosedError. Abort waits for cleanup and returns CancelledError. Unexpected failures reject with SdkDefect

interface CurrentBotApplication
import { links } from "@neontechspace/fluxerly"
export async function applicationExample(client) {
    const application = await client.application.fetch()
    return application.isErr() ? application : links.installation(application.value.id, { permissions: 0n })
}
import { links, type Client } from "@neontechspace/fluxerly"
export async function applicationExample(client: Client) {
    const application = await client.application.fetch()
    return application.isErr() ? application : links.installation(application.value.id, { permissions: 0n })
}

fetch#

Fetch this bot token's application data from /oauth2/applications/@me

Only the documented fields are returned, frozen, without cache storage, gateway events, owner profile lookup or follow-up requests. The timeoutMs option bounds this read only and does not change the gateway startup deadline. Cancelling it affects only this read and never changes the application

fetch(options?: RequestOptions): ResultAsync<
    BotApplication,
    | ConfigurationError | BotApplicationOperationFailure
    | CancelledError
>fetch(
  options?: RequestOptions
): ResultAsync<
  BotApplication,
  | ConfigurationError
  | BotApplicationOperationFailure
  | CancelledError
>

Parameters

options optional
RequestOptions

CustomStatusInput#

Text, emoji and optional expiration for the bot's custom status

Fluxer makes the final checks for emoji availability, Unicode emoji semantics and whether expiration is still in the future

interface CustomStatusInput
emoji optional#

An explicit custom-emoji ID or one Unicode emoji

CustomStatusEmoji
expiresAt optional#

Future ISO-8601 expiration time

Impossible calendar dates are rejected before retaining the update. Fluxer rejects an expiry that passes before it receives the update

string
text optional#

Status text, from 1 through 128 UTF-16 code units

string

GatewayCommands#

Send gateway commands that have no SDK method, on this client's ready shards

This is an advanced escape hatch: The SDK validates the frame locally but does not know the command's meaning, and Fluxer closes the connection, ending the client, for commands it rejects. Observe replies to these commands through the raw event, subscribing before sending

interface GatewayCommands

send#

Send one command frame { op, d } on a Connected local shard

The data is copied through JSON encoding when the operation starts, so later changes to it have no effect, and the encoded frame must be at most 4,096 UTF-8 bytes, Fluxer's message limit. Undefined data is invalid. Pass null instead. Data JSON cannot encode, such as a cycle or a BigInt, fails with reason input. Heartbeat, Identify, Resume and server-only opcodes fail with reason reserved. Commands share the shard's outbound pacing with presence and request commands: At most 500 paced commands per rolling 60 seconds per session, retained across Resume and reset for a fresh Identify, sent in call order. Heartbeats, Identify and Resume are never delayed. At most 500 gateway.send commands may wait per shard, and further submissions fail with reason busy until transmission or cancellation releases queue capacity. Completion means the frame was handed to the connection, not that Fluxer received or accepted it. A sent Request Guild Members command, opcode 8, counts toward the 12 member requests that members.iterateChunks allows this client in any 11 seconds, or the children of one supervisor together, but this method never refuses one for that limit. Fluxer answers an opcode it does not define, missing data or invalid command fields with close code 4001 or 4002. The SDK treats both as permanent, so the client ends. Data must match Fluxer's command schema exactly. Observe any reply through the raw event, which receives every dispatch the session accepts. A shard that loses its connection before sending fails a waiting command with reason notReady, and the SDK never replays it after reconnecting. Each sent command is logged at Debug with code gateway.commandSent

send(
    shardId: number,
    op: number,
    d: unknown,
    options?: OperationOptions
): ResultAsync<
    void,
    ConfigurationError | CancelledError | GatewaySendFailure
>send(
  shardId: number,
  op: number,
  d: unknown,
  options?: OperationOptions
): ResultAsync<
  void,
  | ConfigurationError
  | CancelledError
  | GatewaySendFailure
>

Parameters

shardId
number
op
number
d
unknown
options optional
OperationOptions

Cancellation through options.signal withdraws a command that is still waiting for the pacing budget and returns CancelledError. Withdrawal immediately releases its local queue capacity. A command already handed to the connection cannot be withdrawn. A malformed signal returns ConfigurationError before anything is queued. A throw from the data's own getters or toJSON rejects with SdkDefect code application.defect and the thrown value as its cause. Other unexpected failures reject with SdkDefect

// Commands with an SDK method, such as community counts through client.guilds.fetchCounts, use that method instead
export async function sendCustomCommand(client, command, onReply) {
    // Subscribe before sending so the raw reply cannot be missed
    const replies = client.on("raw", (dispatch) => {
        if (dispatch.t === command.replyType)
            onReply(dispatch)
    })
    try {
        const result = await client.gateway.send(0, command.op, command.data)
        if (result.isErr())
            replies.close()
        // Close the returned subscription once the reply has arrived
        return result.map(() => replies)
    }
    catch (error) {
        replies.close()
        throw error
    }
}
import type { Client, RawDispatch } from "@neontechspace/fluxerly"
// Commands with an SDK method, such as community counts through client.guilds.fetchCounts, use that method instead
export async function sendCustomCommand(
    client: Client,
    command: { op: number; data: unknown; replyType: string },
    onReply: (reply: RawDispatch) => void,
) {
    // Subscribe before sending so the raw reply cannot be missed
    const replies = client.on("raw", (dispatch) => {
        if (dispatch.t === command.replyType) onReply(dispatch)
    })
    try {
        const result = await client.gateway.send(0, command.op, command.data)
        if (result.isErr()) replies.close()
        // Close the returned subscription once the reply has arrived
        return result.map(() => replies)
    } catch (error) {
        replies.close()
        throw error
    }
}

Instance#

Find the selected Fluxer instance's endpoints and build links and asset URLs for it

The resolve method fetches the unauthenticated well-known document only when needed. Concurrent callers share that request. A successful frozen result is retained until client shutdown, without background refresh. Cancelling one caller leaves other callers using the request. The last departing caller waits for discovery cleanup

interface Instance
export async function instanceExample(client) {
    const resolved = await client.instance.resolve({ timeoutMs: 10_000 })
    if (resolved.isErr())
        return resolved
    const channel = resolved.value.links.channel({ id: "1750000000000000000" })
    return { api: resolved.value.endpoints.apiPublic, channel }
}
import type { Client } from "@neontechspace/fluxerly"
export async function instanceExample(client: Client) {
    const resolved = await client.instance.resolve({ timeoutMs: 10_000 })
    if (resolved.isErr()) return resolved
    const channel = resolved.value.links.channel({ id: "1750000000000000000" })
    return { api: resolved.value.endpoints.apiPublic, channel }
}

resolve#

Resolve this client's instance endpoints and its pure link and asset URL helpers

The unauthenticated discovery request is shared with other resolves, REST work or gateway startup. The timeoutMs option defaults to 30,000 for this wait only. Cancellation releases only this wait, while other users can keep the shared discovery read alive. Document, rate-limit, timeout, closure and invalid timeout-option failures are expected failures

resolve(options?: RequestOptions): ResultAsync<
    ResolvedInstance,
    InstanceResolveError | CancelledError
>resolve(
  options?: RequestOptions
): ResultAsync<
  ResolvedInstance,
  | InstanceResolveError
  | CancelledError
>

Parameters

options optional
RequestOptions

Unexpected cleanup failures reject with SdkDefect, retaining safe details of any accompanying failure or interruption

InstanceDomainMigration#

A web domain migration announced by instance discovery, such as hosted Fluxer moving its web app to a new domain

The SDK records an enabled migration at Info when a client first resolves the instance. It changes no endpoint itself

interface InstanceDomainMigration
anonymousRolloutBasisPoints#

Share of signed-out web visitors moved to the new domain, in basis points from 0 through 10,000, or null when not supplied

number | null
enabled#

Whether the instance switched the migration on

boolean
standaloneForwarding#

Whether standalone app installs are forwarded to the new domain, or null when not supplied

boolean | null

InstanceEndpoints#

Where the selected Fluxer instance hosts its services, validated from its discovery document

Services may use different origins, which selecting that instance trusts

interface InstanceEndpoints
apiPublic#

Public HTTP API base

The SDK appends its documented /v1 route once, preserving any advertised prefix

string
gateway#

Gateway WebSocket base

The SDK adds its required protocol query without deriving another host

string
invite#

Invite base for instance-specific invite and vanity URLs

string
media#

Public media base for instance-specific generated asset URLs

string
staticCdn#

Static CDN base for instance-specific generated default-asset URLs

string
webapp#

Web application base for instance-specific generated navigation and installation links

string

Presence#

Set the bot's displayed status or request updates for selected community members

Outgoing status is sent to live shards assigned to this client, at least four seconds apart per shard. Member selections use separate bounded gateway requests. Neither operation has a provider acknowledgement or recipient-delivery guarantee. The SDK does not fetch membership or filter out self. Fluxer decides access and which updates are visible

interface Presence

set#

Set the bot's requested status and optional custom status, including before connecting

The input is validated and frozen when the call runs. Omitted customStatus keeps the previous request, and null clears it. Expired custom statuses are not restored after reconnect. Each shard retains at most one unsent status update, replacing it with the latest intent even while gateway pacing waits. The four-second interval starts when an update reaches the socket, not when it is queued. Success means the request was accepted locally and scheduled for each shard, not acknowledged by Fluxer. Shutdown releases this intent and its timer

set(input: PresenceInput): Result<void, PresenceFailure>set(
  input: PresenceInput
): Result<void, PresenceFailure>

Parameters

Returns
Result<void, PresenceFailure>
Fails with

Returns a Result synchronously. A throwing input getter throws SdkDefect with code application.defect and the thrown value as its cause, and other unexpected failures throw SdkDefect with code sdk.defect

import { MemberMentionPreferences } from "@neontechspace/fluxerly"
export async function botProfileExample(client, guildId) {
    const presence = client.presence.set({ status: "online", customStatus: { text: "Ready", emoji: { name: "🌱" } } })
    if (presence.isErr())
        return presence
    return client.members.editSelf(guildId, { nickname: "Support", mentionFlags: MemberMentionPreferences.PreferNoMention })
}
import { MemberMentionPreferences, type Client } from "@neontechspace/fluxerly"
export async function botProfileExample(client: Client, guildId: string) {
    const presence = client.presence.set({ status: "online", customStatus: { text: "Ready", emoji: { name: "🌱" } } })
    if (presence.isErr()) return presence
    return client.members.editSelf(guildId, { nickname: "Support", mentionFlags: MemberMentionPreferences.PreferNoMention })
}

setMembers#

Request presence updates for selected members in one community

Register a presenceUpdate listener, then pass accessible non-self member IDs. Pass [] to clear the selection. Closing the listener does not clear it. No full-member subscription, member lookup or presence cache is created, and Fluxer remains authoritative for access and filtering. The community must belong to a shard assigned to this client, or the call fails with PresenceError reason input

setMembers(
    guildId: string,
    memberIds: readonly string[]
): Result<void, PresenceFailure>setMembers(
  guildId: string,
  memberIds: readonly string[]
): Result<void, PresenceFailure>

Inputs are copied and retained when the call runs. Up to 1,000 distinct decimal IDs are accepted, but the full UTF-8 gateway frame must fit 4,096 bytes. Long IDs therefore reduce the effective per-community maximum. The client retains selections for at most 100 communities and 10,000 IDs. Clearing an unsent selection withdraws its queued command and releases its slot immediately. Changes to a selection that has not reached the socket replace that unsent request. Clearing a selection already sent keeps one bounded session slot until fresh Identify or confirmed community leave, because a local socket write cannot confirm that Fluxer applied a clear

After READY or RESUMED, the latest selection or clear is combined and sent at most once per 125 ms per shard. This interval starts at actual socket transmission, and at most one member-selection command waits per shard. Sending the same list again deliberately requests a refresh. A matching guild creation also retries the latest selection or a previously sent clear. None of these attempts guarantees an event or proves provider acceptance. Initial state and transitions can be missed during recovery. Loss of shared channel visibility can remove the provider subscription, so resend the selection after access returns. Clear explicitly or shut down the client to release local intent. Input or limit failures fail with PresenceError, and closing clients fail with ClientClosedError. The community and member IDs must be positive decimal strings without leading zeros, no greater than 9223372036854775807. The member IDs are copied once, and that copy is validated and subscribed

Parameters

guildId
string
memberIds
readonly string[]
Returns
Result<void, PresenceFailure>
Fails with

Returns a Result synchronously. A throwing ID getter throws SdkDefect with code application.defect and the thrown value as its cause, and other unexpected failures throw SdkDefect with code sdk.defect

export function watchSelectedMember(client, guildId, memberId) {
    const subscription = client.on("presenceUpdate", (presence) => {
        if (presence.guildId === guildId && presence.userId === memberId)
            void presence.status
    })
    const selected = client.presence.setMembers(guildId, [memberId])
    if (selected.isErr())
        subscription.close()
    return selected.map(() => subscription)
}
import type { Client } from "@neontechspace/fluxerly"
export function watchSelectedMember(client: Client, guildId: string, memberId: string) {
    const subscription = client.on("presenceUpdate", (presence) => {
        if (presence.guildId === guildId && presence.userId === memberId) void presence.status
    })
    const selected = client.presence.setMembers(guildId, [memberId])
    if (selected.isErr()) subscription.close()
    return selected.map(() => subscription)
}

PresenceInput#

Set the status this bot should publish on its gateway connections

Use customStatus: null to request clearing the custom status. Omission preserves this client's latest custom-status request, or leaves the provider value unchanged if there is no retained request. The client copies the latest accepted settings and retains at most one unsent update per local shard, replacing its payload with the latest intent. Status updates are at least four seconds apart, measured from each actual socket transmission, not queue admission. It publishes those settings on ready local shards and restores them after READY or RESUMED. A successful call means the client accepted the settings. It does not mean every shard published them at once or that another user can see them. Shutdown clears the saved settings and pending local timers

interface PresenceInput
customStatus optional#

New custom status, null to clear it, or omission to retain the latest custom-status request made by this client

CustomStatusInput | null
status#

Visible status to publish on the bot's live gateway connections

PresenceStatus

ResolvedInstance#

The selected server's service addresses and matching URL helpers

This frozen result is retained for one client, not refreshed on each later resolve. Use assets and links to build URLs for this instance rather than the hosted default. These helpers make no requests, inspect no credentials and do not refresh discovery or change caches

interface ResolvedInstance
apiCodeVersion#

Provider code-version indicator from discovery

It is not an API path version

number
assets#

Pure asset URL helpers bound to this instance's media and static-CDN bases

They follow the assets namespace's input-field and fallback rules, return URLs directly and throw AssetUrlError for invalid input

AssetHelpers
discoveryEnabled#

Whether this instance has community directory discovery switched on

True when discovery omits the flag, as Fluxer documents. A present value that is not a boolean makes the discovery document invalid. When false, the discovery namespace's search, apply, edit and withdraw operations fail with reason rejected, HTTP 400 and details.providerCode DISCOVERY_DISABLED, while fetchStatus and fetchCategories still answer. The value is a snapshot from when the client resolved the instance, so an operator's later change appears only in a new client

boolean
domainMigration#

The web domain migration that discovery announced, or null when the instance announced none

Links and OAuth URLs always use the discovered web application base in endpoints, so they follow a migrated domain

InstanceDomainMigration | null
endpoints#

Exact validated service bases from discovery

InstanceEndpoints
presignedAttachmentUploads#

Whether this instance advertises temporary upload URLs for attachment transfers

boolean

RestRequest#

One request to a Fluxer API route that the SDK does not wrap, sent with the client's bot credential through the same queue, rate-limit learning, 429 waits, deadline, logging and cancellation as wrapped operations

Invalid input fails before dispatch with RestRequestError reason input. A GET that fails with a network error or HTTP 500, 502, 503 or 504 is retried at most twice. Another method is sent again only after Fluxer confirms a rate limit with HTTP 429, so a write with an unknown outcome is never repeated automatically. Message and resource caches are neither read nor updated, and Fluxer's rate limits and permissions still apply

interface RestRequest extends OperationOptionsinterface RestRequest
  extends OperationOptions

Aborting the signal stops waiting and cancels the HTTP exchange, but cannot undo a request Fluxer already received

export async function currentUserName(client) {
    const result = await client.rest.request({ method: "GET", path: "/users/@me" })
    return result.isOk() ? result.value.body.username : undefined
}
import type { Client } from "@neontechspace/fluxerly"
export async function currentUserName(client: Client): Promise<string | undefined> {
    const result = await client.rest.request<{ username: string }>({ method: "GET", path: "/users/@me" })
    return result.isOk() ? result.value.body.username : undefined
}
auditReason optional#

Audit-log reason sent as the X-Audit-Log-Reason header, 1 through 512 printable ASCII characters after trimming

Omission sends no header. Never included in errors, logs or diagnostics

string
body optional#

JSON body, serialized when the request starts

A value JSON cannot represent fails with reason input. Its size counts against the client's queued JSON budget while the request waits. Not allowed with GET

unknown
files optional#

Files sent inline as multipart/form-data, with the JSON body as payload_json and each file as files[n] in order

Accepts the same byte, file and stream inputs as message attachments, with the same size, filename and upload-budget rules. The body must then be omitted or a JSON object without attachments, because the SDK sets attachments to the files' metadata as Fluxer's message routes expect. A request whose file source cannot be read again fails with reason rateLimit instead of waiting out a 429. Not allowed with GET

readonly AttachmentInput[]
method#

HTTP method

Only POST, PUT, PATCH and DELETE may carry a body or files

"GET" | "POST" | "PUT" | "PATCH" | "DELETE"| "GET" | "POST" | "PUT" | "PATCH"
| "DELETE"
path#

API path after /v1, such as /users/@me, of at most 2,048 characters

It must start with one slash and contain only URL path characters with valid percent escapes, without empty, dot or dot-dot segments, a query or a fragment. The /v1 prefix and token-authenticated webhook routes (/webhooks/{id}/{token}) are rejected, including percent-encoded unreserved characters in those route names. These checks are case-sensitive and do not decode reserved characters. Logs, metrics and spans name the route with numeric IDs and other non-word segments replaced by placeholders

string
query optional#

Query parameters appended to the path in insertion order

Undefined values are omitted, numbers must be finite, and booleans are sent as true or false. The encoded query may contain at most 8,192 characters

Readonly<Record<string, string | number | boolean | undefined>>Readonly<
  Record<
    string,
    | string | number | boolean
    | undefined
  >
>
timeoutMs optional#

Total milliseconds across local queueing, rate-limit waits, retries and HTTP

An integer from 1 through 2,147,483,647, defaulting to the client's rest.defaultTimeoutMs (30,000 unless configured). Owned cleanup is awaited afterward, so completion can take longer

number

Inherited from OperationOptions: signal

RestRequests#

Send requests to Fluxer API routes that the SDK does not wrap, with the client's bot credential and its REST scheduler

Calls start immediately, need no gateway connection and default to the client's rest.defaultTimeoutMs deadline. Abort waits for cleanup and returns CancelledError. Unexpected failures reject with SdkDefect

interface RestRequests

request#

Send one request to a route under /v1 and resolve with its status, lower-case headers and parsed JSON body

The request waits in the client's queue and learned rate-limit windows like wrapped operations, and fails at once with reason rateLimit and the remaining wait as retryAfterMs when a window outlasts the deadline. A 429 is waited out and sent again while the deadline allows. A GET is also retried at most twice after a network error, including a connection lost while the response body arrives, or HTTP 500, 502, 503 or 504. Another method is never sent again after an uncertain outcome. The body is sent as given, including a lone surrogate, which Fluxer answers with HTTP 503. Invalid input fails with RestRequestError reason input before any request, a non-2xx status fails with its status and sanitized apiError, and closure fails with ClientClosedError. Message and resource caches are not updated. Percent-encoded unreserved letters cannot bypass the reserved /v1 prefix or token-webhook route checks, which remain case-sensitive. Network causes retain sanitized transport facts, not raw errors. Unknown uppercase provider codes remain in details.providerCode, including on HTTP 429 when its wait exceeds the deadline. Logs, metrics and spans name only the route template, never the query, body or response

request<T = unknown>(input: RestRequest): ResultAsync<
    RestResponse<T>,
    ConfigurationError | RestRequestFailure | CancelledError
>request<T = unknown>(
  input: RestRequest
): ResultAsync<
  RestResponse<T>,
  | ConfigurationError
  | RestRequestFailure
  | CancelledError
>

Parameters

RestResponse#

A successful response from client.rest.request, for any 2xx status

interface RestResponse<T = unknown>
body#

The parsed JSON body, or undefined when the response has no body

The type parameter is not checked at runtime, so decode the value before trusting its shape. A body over 16 MiB or one that is not valid JSON fails the request with reason response

T
headers#

Response headers with lower-case names

Repeated headers are joined with a comma and space as fetch does

Readonly<Record<string, string>>
status#

HTTP status from 200 through 299

number

ScheduleOptions#

Choose when client.schedule runs a task, in both entry points

Without options the task runs once, as soon as possible

interface ScheduleOptions
delayMs optional#

Milliseconds before the first run, an integer from 0 through 2,147,483,647

A repeating task waits intervalMs by default, like an interval timer, and a task that runs once waits 0

number
intervalMs optional#

Repeat the task, waiting this many milliseconds after each run ends before the next one starts, an integer from 1 through 2,147,483,647

Runs therefore never overlap. Without it the task runs once

number

ScheduledTask#

A task started with client.schedule

It ends by itself after its only run, or when the client shuts down

interface ScheduledTask

close#

Cancel every later run

A run in progress continues, and a draining shutdown still waits for it. The task can close itself from its own run. Repeated calls do nothing

close(): void

ShutdownOptions#

Choose how shutdown treats work that is still running, in both entry points

interface ShutdownOptions
drainMs optional#

Milliseconds to let running work finish before shutdown cancels it, an integer from 0 through 2,147,483,647

The default 0 cancels running handlers at once. The runBot function drains for 5,000 ms by default when asked to stop

number

During the drain the client accepts no new events. New subscriptions and event waits start closed, as after shutdown. Running handlers and commands continue, events already waiting in on handler queues still run, and REST requests continue, including new ones that handlers send. Scheduled task runs in progress continue, while tasks start no new run and new tasks are refused. The connection state stays as it was until the drain ends. The drain ends as soon as no handler or scheduled task is running, no event is waiting and no REST request is in progress, logging lifecycle.drained, or when the time runs out. Then shutdown continues as usual and cancels what is left, logging how many handlers, events, tasks and requests it cut off as a lifecycle.drainTimedOut Warn record. Collectors and subscribe subscriptions receive no events during the drain and end with the shutdown. A shutdown call while another is in progress shares that shutdown and its drain.

A default API handler or scheduled task that awaits a draining shutdown of its own client keeps the drain waiting until the time runs out, so start the shutdown without awaiting it there. A native handler, collector callback or scheduled task that calls shutdown ends at once, and the drain continues for the other work. An invalid value throws ConfigurationError in the default API and dies with it in the Effect API, before shutdown starts

StateObserver#

Stop receiving connection-state updates from client.observeState

Use using to close the observer when the enclosing block ends

interface StateObserver

[dispose]#

Close this observer, as using does at the end of a block

[dispose](): void

close#

Stop delivering states and drop a pending one

Listener code already running is not cancelled. Repeated calls do nothing

close(): void

Type Aliases#

BotEventContext#

Data and operations available to one configured bot event handler

type BotEventContext<
    K extends EventName,
    M extends MessageCore = Message
> =
    & {
          readonly client: Client<M>
          readonly context: EventContext<K, M>
          readonly event: EventMap<M>[K]
          readonly signal: AbortSignal
      }
    & (K extends "messageCreate"
          ? {
                readonly message: M
                readonly reply: (
                    input: ReplyInput | string,
                    options?: Omit<RequestOptions, "signal">
                ) => ReturnType<Client<M>["messages"]["reply"]>
            }
          : { … })type BotEventContext<
  K extends EventName,
  M extends MessageCore = Message
> =
  & {
      readonly client: Client<M>
      readonly context:
        EventContext<K, M>
      readonly event:
        EventMap<M>[K]
      readonly signal: AbortSignal
    }
  & (K extends "messageCreate"
      ? {
          readonly message: M
          readonly reply: (
            input:
              | ReplyInput
              | string,
            options?: Omit<
              RequestOptions,
              "signal"
            >
          ) => ReturnType<
            Client<M>["messages"][
              "reply"
            ]
          >
        }
      : { … })

BotEvents#

One optional handler per event, as a function or an object with a handler and delivery settings

When a queue fills, runBot drops the oldest waiting event, logs a Warn record and counts the drop, and the bot keeps running. With a partition, the overflow option's rule decides which event is dropped. Handlers for messageCreate run up to eight at a time by default, so they can finish out of order, and other events run one at a time. A failed handler is reported with its original error to onError, or logged in full, without stopping the bot or retrying that handler

type BotEvents<M extends MessageCore = Message> = {
    readonly [K in EventName]?:
        BotEventHandler<K, M> | BotEventOptions<K, M>
}type BotEvents<
  M extends MessageCore = Message
> = {
  readonly [K in EventName]?:
    | BotEventHandler<K, M>
    | BotEventOptions<K, M>
}

ConnectionState#

The client's current gateway connection state, not a history of events

Disconnected permits startup. Connecting includes startup retries until every shard owned by this client is ready at the same time. Connected means every local shard is ready. Recovering means an established shard lost readiness and lasts until all shards owned by this client are ready again. Other ready shards can continue work during Recovering. Closing means permanent cleanup is underway, and Closed requires a new client to connect again

type ConnectionState =
    | "Disconnected" | "Connecting" | "Connected" | "Recovering"
    | "Closing" | "Closed"type ConnectionState =
    | "Disconnected" | "Connecting"
    | "Connected" | "Recovering"
    | "Closing" | "Closed"

CustomStatusEmoji#

One custom-status emoji

The variants cannot be combined

type CustomStatusEmoji =
    { readonly id: string } | { readonly name: string }type CustomStatusEmoji =
    | { readonly id: string }
    | { readonly name: string }

PresenceStatus#

The bot's visible status while connected, with dnd meaning do not disturb

Use invisible to hide online status, not to disconnect the client. The status offline is excluded because Fluxer normalizes it to invisible on a connected session

type PresenceStatus = "online" | "idle" | "dnd" | "invisible"type PresenceStatus =
    | "online" | "idle" | "dnd"
    | "invisible"