Interface in Client and Lifecycle · Effect API
A bot client whose asynchronous methods return descriptions of work as Effects or Streams
interface Client<M extends MessageCore = Message>interface Client<
M extends MessageCore = Message
>Execute Effects with yield* inside Effect.gen or with an Effect runner. Consume Streams to start their work. Each operation uses the caller's Effect services and cancellation. The scope that creates the client owns its connection work and permanent cleanup. Expected errors use Effect's error channel. Unexpected faults and cancellation remain in Cause
Cleanup defects stop retries and preserve any operation failure or interruption alongside the defect in Cause. Effect reports an operation failure that occurs together with a defect as a defect that keeps the failure's value. Successful REST JSON responses are limited to 16 MiB before parsing. This is not a total memory limit
Upload planning and completion responses have a separate 1 MiB limit
A confirmed HTTP 429 response with a valid retry delay pauses all API routes on this client when X-RateLimit-Global is true, X-RateLimit-Scope is global, or the JSON body has global: true. A valid global indicator takes priority over conflicting route-specific metadata. Malformed scope values are ignored
A global header with a valid Retry-After starts the pause before body inspection, including when the body is missing, malformed, oversized or too slow. Body inspection stays bounded to 8 KiB and 100 ms before awaited cleanup. Without global metadata, an identified route limit pauses only its rate-limit group
A 429 that identifies no limiter pauses all API routes on this client, and repeated unidentified limits can lengthen the pause. Without a usable delay, the call fails rather than guessing how long to wait. Rate-limit waits count toward each call's original deadline. Interruption removes only that call from the queue, not the shared pause
A call whose known pause outlasts its deadline fails at once with reason rateLimit and the remaining wait as retryAfterMs instead of waiting. Clients under one supervisor share pauses Fluxer declares global. Other clients do not coordinate these waits, and an unidentified limit pauses only its own client
Attachment downloads do not wait for API rate limits. Writes retry only confirmed rate-limit rejection, never an uncertain outcome
Valid X-RateLimit-Bucket metadata updates how this client groups rate-limited routes, without application configuration. Known Fluxer templates retain their channel, community, user, webhook or invite resource partitions. Unknown route templates share one rate-limit bucket when they report the same identifier, to avoid exceeding a possible shared limit
Initial requests can still receive 429 before the server's grouping is learned. Each client tracks at most 2,048 route aliases and 2,048 bucket states. Idle aliases expire after five minutes unless they preserve an active pause. Old responses cannot undo newer mappings or reopen an exhausted window
A changed mapping preserves any known prior pause until it expires. When tracking reaches capacity, the client drops observations that are not blocking requests first. If active pauses fill capacity or a route cannot be grouped safely, the client waits rather than ignoring a known limit. Closing the client clears this temporary state
Each gateway connection accepts uncompressed text messages up to 100 MiB (104,857,600 bytes), counting all fragments together. The transport checks this size before decoding UTF-8 or parsing JSON. This keeps the previous transport default. This is not a Fluxer server-to-client maximum, an outbound command limit, a subscription budget or a JavaScript heap bound
Larger messages fail with ConnectionError, phase gateway, reason protocol and status 1009. Invalid UTF-8 uses status 1007. Neither failure retries automatically. A failed standalone connect waits for cleanup and allows another explicit connect. A failed managed run or established connection ends the client lifetime. Use waitForClose to observe later failures
Properties#
application#
Read documented fields for the authenticated bot's application, without owner details or management operations
readonly application: CurrentBotApplicationattachments#
Refresh signed attachment URLs explicitly or download from this instance's discovered media path with byte limits
readonly attachments: AttachmentsauditLogs #
Read filtered audit pages or bounded entry scans, without retaining an audit cache
readonly auditLogs: AuditLogscache#
Enumerate or release locally cached data
Caching is disabled unless enabled in ClientOptions.cache
readonly cache: ClientCache<M>channels#
readonly channels: ChannelsdirectMessages #
Open and manage one-to-one or group conversations, or send a direct message
Use messages for other content operations once the channel IDs are known
readonly directMessages: DirectMessages<M>readonly directMessages: DirectMessages<
M
>discovery#
Search the public community directory and manage listings, without joining communities or finding instance endpoints
readonly discovery: Discoveryemojis#
Manage custom emoji and optionally look up their cached metadata
readonly emojis: Emojisgateway#
readonly gateway: GatewayCommandsgatewayLatencyMs #
The slowest current heartbeat round-trip time in milliseconds across this client's gateway shards
Null unless the client is Connected and every local shard has a current heartbeat acknowledgement. Connection loss, recovery and shutdown clear the affected shard's measurement until its new connection receives an acknowledgement. Zero is a valid measurement, not a marker for unavailable data
readonly gatewayLatencyMs: number | nullreadonly gatewayLatencyMs:
| number | nullguilds#
Read community data and memberships, or use the optional local community cache
readonly guilds: Guildsinstance#
Find this client's instance endpoints and get pure asset and link URL helpers for that instance
readonly instance: Instanceinvites#
Inspect, create, list and revoke invite codes, without using them to join or retaining codes
readonly invites: InviteslastReceipt #
The receipt of the most recently accepted event, as EventContext.receipt numbers events, or 0 before the first
It is read live, counts every event the client accepted whether or not a handler receives it, and is not reset by reconnects. Events refused during a draining shutdown get no receipt
Work that is not an event, such as a timer or a command's follow-up, can use it to find its place in the event order: Record it when the work starts, and treat any event whose receipt is larger as newer than the work, such as a later edit that makes the work's result stale
Under a supervisor each child process numbers its own events, so values from different processes cannot be compared
readonly lastReceipt: numberconst edited = new Map()
export function trackEdits(client) {
return client.on("messageUpdate", (message, _signal, context) => void edited.set(message.id, context.receipt))
}
export async function summarize(client, messageId, work) {
const startedAt = client.lastReceipt
const summary = await work()
// An edit accepted after the work started makes the summary stale
return (edited.get(messageId) ?? 0) > startedAt ? undefined : summary
}
import type { Client } from "@neontechspace/fluxerly"
const edited = new Map<string, number>()
export function trackEdits(client: Client) {
return client.on("messageUpdate", (message, _signal, context) => void edited.set(message.id, context.receipt))
}
export async function summarize(client: Client, messageId: string, work: () => Promise<string>) {
const startedAt = client.lastReceipt
const summary = await work()
// An edit accepted after the work started makes the summary stale
return (edited.get(messageId) ?? 0) > startedAt ? undefined : summary
}
logging#
Change the log level and per-category levels while the client runs
readonly logging: ClientLoggingmembers#
readonly members: Membersmessages#
Read, send, edit and delete messages, use the optional message cache, or collect future messages and reactions
readonly messages: Messages<M>permissions#
Calculate permission flags locally or from fresh resource reads, without caching decisions
readonly permissions: PermissionHelperspresence#
Set bot status or select member presence updates, restored after gateway reconnects but not stored across process restarts
readonly presence: Presencerest#
Send requests to Fluxer API routes without an SDK method, through this client's shared request scheduling
readonly rest: RestRequestsroles#
readonly roles: Rolesshards#
Frozen connection state and heartbeat measurements for this client's shards, in configured local ID order
An unsharded client contains only shard ID zero until it moves to automatic sharding, and an automatically sized client contains none until its first connect has chosen the plan. Both contain the new plan's shards after a move. Inspect this to identify a recovering shard even while other shards remain ready. It excludes shards owned by other processes and does not establish whole-bot state or cross-process ordering
readonly shards: readonly ShardState[]state#
Current connection state, controlled by the SDK rather than the consumer
readonly state: ConnectionStatestickers#
Manage custom stickers and optionally look up their cached metadata
readonly stickers: Stickersthreads#
Create threads and forum posts, change, list and search threads, and manage thread members
readonly threads: Threads<M>users#
Fetch public account data or look up an account in the optional local cache
readonly users: Userswebhooks#
Manage webhooks with the bot's permissions, returning metadata without tokens
readonly webhooks: WebhooksMethods#
connect#
Connect the gateway and complete when every shard assigned to this client has authenticated and received READY
This does not wait for GUILD_CREATE, a full community roster or all resources to load. Use connect when startup must finish separately from the client's lifetime. After success, the connection and automatic recovery continue until shutdown or permanent failure, and waitForClose observes that later outcome
connect(): Effect<void, ConnectError>connect(): Effect<
void, ConnectError
>The call controls startup only, using the client's connection settings. Cancellation or an expected failure before initial readiness waits for shard cleanup and leaves the client Disconnected, so connect can be tried again. An already-connected client not managed by run succeeds without opening another socket
A competing call fails with ClientBusyError without disturbing active work, and closing clients fail with ClientClosedError. After initial readiness, permanent failure of a required shard in a multi-shard plan closes the client. The waitForClose method retains that failure as ShardConnectionError with shardId and failure
- Returns
Effect<void, ConnectError>- Fails with
diagnostics#
Read a frozen local report of active work, queues and cache occupancy
No request, telemetry or persistent record is created. The report excludes tokens, raw request paths, resource IDs and payloads. A rate-limit hold may name a method and route template with IDs replaced, such as "POST /channels/:id/
Event counts include open sources, message and reaction collectors and executing subscription handlers, not an enforced client-wide admission quota. Accounted cache and queue bytes are not heap memory, process memory or remote storage. After closure, configured limits remain visible and retained counts show cleanup progress. These values do not prove remote completeness or gateway readiness
This runs immediately and takes no signal
diagnostics(): ClientDiagnostics- Returns
ClientDiagnostics
observeState #
Receive the current connection state, then later state updates
While a subscriber is busy, only the newest pending state is kept. Slow subscribers can miss intermediate states without delaying recovery. Ending the subscription drops pending delivery without stopping the client. Use waitForClose, not state changes, to observe the client's terminal failure
observeState(): Stream<ConnectionState>observeState(): Stream<
ConnectionState
>- Returns
Stream<ConnectionState>
Returns a Stream in which each consumer gets a coordinated initial snapshot. Closed ends the Stream
on#
Register a handler for future events of one type, before or after connecting the client
Use the returned Subscription to close it and observe waitForClose. No history is replayed and HTTP operations do not create events locally. Bulk deletion events do not also call messageDelete handlers. Enabled cache updates happen before handlers and do not depend on a subscription succeeding, including its overflow
on<
E,
R,
E2 = never,
R2 = never,
K extends keyof EventMap<Message> = "messageCreate"
>(
event: K,
handler: (event: EventMap<M>[K], context: EventContext<K, M>)
=> Effect<unknown, E, R>,
options?: EventHandlerOptions<E2, R2, EventMap<M>[K]>
): Effect<Subscription, never, Scope | R | R2>on<
E,
R,
E2 = never,
R2 = never,
K extends keyof EventMap<Message>
= "messageCreate"
>(
event: K,
handler: (
event: EventMap<M>[K],
context: EventContext<K, M>
) => Effect<unknown, E, R>,
options?: EventHandlerOptions<
E2, R2, EventMap<M>[K]
>
): Effect<
Subscription, never,
Scope | R | R2
>Handlers run one at a time by default, except messageCreate handlers and partitioned handlers, which run up to eight at a time as runBot does. A slow messageCreate callback therefore does not stall later messages, but callbacks can finish out of order
Pass concurrency 1 or a partition such as "channel" to keep related messages in order. Without a partition, increasing concurrency keeps receive-order starts but allows out-of-order completion. A partition keeps receive order within each key, while a later event for another key may start first
Each subscription has its own ordering and queue, with no ordering across event types and no exactly-once delivery. Default pending limits are 256 payloads or 4 MiB of full source JSON, not total process memory. A bulk payload counts once, including all its bytes
A full queue drops its oldest waiting events by default, logs a Warn record and counts the drop, and the subscription keeps running. With a partition, it drops from the longest backlog or the arriving event instead, as the overflow option describes. An event larger than maxPendingBytes that has to wait is itself dropped
Choose overflow stop to close only this subscription instead, observable through the returned handle
A failed handler is isolated and not retried. A default API handler fails when it throws, rejects, or returns or resolves an Err result, and a native handler fails when its Effect fails
Its original failure, with the event name, subscription ID and message IDs, goes to this subscription's onError, else the client-level onError, else an Error log record with the full message, stack and cause chain. Observe the subscription's outcome as well as the client's run or waitForClose outcome
Registration misuse, such as an unknown event name, fails with a suggested correction
Each invocation also receives an EventContext with the receiving shard, its session epoch, its receipt, the received
frame size and, for an event that changes or removes a cached entity, the previous snapshot of the enabled cache.
Event middleware registered with use runs around every handler invocation, including those of subscriptions registered earlier
Parameters
event-
K handler-
(event: EventMap<M>[K], context: EventContext<K, M>) => Effect<unknown, E, R>( event: EventMap<M>[K], context: EventContext<K, M> ) => Effect<unknown, E, R> optionsoptional-
EventHandlerOptions<E2, R2, EventMap<M>[K]>EventHandlerOptions< E2, R2, EventMap<M>[K] >
- Returns
Effect<Subscription, never, Scope | R | R2>Effect< Subscription, never, Scope | R | R2 >
The handler receives the event payload and the EventContext, and returns the Effect to run. The subscription belongs to the Scope and context that execute this Effect, without a separate SDK runtime. Registration misuse is a defect carrying ConfigurationError
Registration after shutdown began, such as from a handler still being interrupted by shutdown, is not misuse: It returns an already-closed Subscription whose handler never runs and whose waitForClose succeeds, and writes a Warn log record with code events. Handler failures are reported with their complete Cause through the Effect logger
Client or registration-scope closure interrupts handlers and awaits handler cleanup
import { Cause, Effect } from "effect"
import type { Client, Message } from "@neontechspace/fluxerly/effect"
export function debugHandlerExample<E, R>(
client: Client,
handle: (message: Message) => Effect.Effect<void, E, R>,
inspectFailure: (cause: Cause.Cause<E>) => void,
) {
return client.on("messageCreate", (message) =>
Effect.suspend(() => handle(message)).pipe(
// Inspect locally. The failure still reaches onError or the log afterwards
Effect.tapCause((cause) => Effect.sync(() => inspectFailure(cause))),
),
)
}
run#
Start and maintain a gateway connection for the client's whole lifetime, including transient recovery, until shutdown, permanent failure or cancellation
Use run when one owner should control startup, lifetime observation and permanent cleanup together. Once run is accepted, its end always leaves the client Closed after cleanup. Success means normal shutdown, not merely reaching READY. A permanent required-shard failure after readiness closes a multi-shard client with ShardConnectionError
run(): Effect<void, ConnectError>The run method requires a Disconnected client with no competing connection work.
A rejected or pre-cancelled call does not take over or close the client.
Expected connection, busy and closed failures use ConnectError
- Returns
Effect<void, ConnectError>- Fails with
The Effect stays pending for the client's lifetime, and interrupting an accepted run stops that lifetime and waits for cleanup. Unexpected faults and interruption remain in the Cause, alongside any cleanup faults
import { Effect } from "effect"
import { createClient } from "@neontechspace/fluxerly/effect"
export const runBot = (token: string) => Effect.scoped(
Effect.gen(function* () {
const client = yield* createClient({ token })
yield* client.run()
}),
)
schedule#
Run a task later, once or repeatedly, as part of this client's lifetime instead of an event handler or command
Scheduling returns at once, so a handler or command that schedules a task finishes without waiting for it and does not hold its slot. Without options the task runs once as soon as possible, which suits background work started in runBot setup. The delay and interval follow the Clock of the client's other timers, and the interval counts from the end of each run, so runs never overlap
schedule<E = never, R = never>(
task: Effect<unknown, E, R>,
options?: ScheduleOptions
): Effect<ScheduledTask, never, Exclude<R, Scope>>schedule<E = never, R = never>(
task: Effect<unknown, E, R>,
options?: ScheduleOptions
): Effect<
ScheduledTask, never,
Exclude<R, Scope>
>A failed run is reported with its original error to the client-level onError, else an Error log record with code
lifecycle.taskFailed. A repeating task keeps its schedule after a failed run, so a passing problem such as a network
error does not end it, and a lasting one is reported at every run
Shutdown cancels the runs that have not started. A draining shutdown, such as a stop requested from runBot, starts no new run and lets runs in progress finish within its drainMs, and then shutdown cancels what is left. One lifecycle Info record counts the tasks that shutdown ended with a run waiting or in progress
Tasks are kept in memory only, so a restart forgets them. Scheduling after shutdown began, including during a drain, is refused with ClientClosedError, and invalid options are misuse with ConfigurationError
Parameters
task-
Effect<unknown, E, R> optionsoptional-
ScheduleOptions
- Returns
Effect<ScheduledTask, never, Exclude<R, Scope>>Effect< ScheduledTask, never, Exclude<R, Scope> >
The task is an Effect that runs again for each run, with the services available when this Effect executes and a Scope of its own for each run, closed when the run ends. The task does not belong to the executing Scope, so a handler that schedules it can end first
A failed run reports its complete Cause, and a run that interrupts itself ends the task without a report. Shutdown interrupts a run in progress and awaits its finalizers. A defect in those finalizers, such as a failed release, is a cleanup fault: It fails shutdown and is logged as lifecycle
A task that is not an Effect and invalid options are defects carrying ConfigurationError, and scheduling after shutdown began is a defect carrying ClientClosedError
import { Effect } from "effect"
import type { Client } from "@neontechspace/fluxerly/effect"
export function hourlyStatusExample(client: Client, channelId: string) {
return client.schedule(client.messages.send(channelId, "Still running").pipe(Effect.asVoid), {
intervalMs: 3_600_000,
})
}
shardIdForGuild #
Return the ID of this client's shard that receives a community's gateway events, or undefined when another process owns that shard
Fluxer calls a community a guild, so the argument is a guild ID. An automatically sized client returns undefined until its first connect has chosen the plan, and the answer can change when automatic sharding moves to a larger plan, including the first move of a client without sharding settings. The snowflakes.shardFor helper computes the same routing for any plan. Throws ConfigurationError when the ID is not a decimal string
shardIdForGuild(guildId: string): number | undefinedshardIdForGuild(
guildId: string
): number | undefinedParameters
guildId -
string
- Returns
number | undefined
shutdown#
Permanently close this client and wait for startup, recovery, sockets and request cleanup
Shutdown stops credential use and releases cached references, presence intent and expiry timers. Keeping the client or a failure report keeps the token string used to mask logs in memory. Active handlers and message and reaction collector callbacks are cancelled
Pass drainMs to first stop accepting events and let running handlers and REST requests finish for up to that many milliseconds, as runBot does when asked to stop. See ShutdownOptions
Events still waiting in a subscription's queue are discarded, with one events.discarded Info record for each subscription that had any, and a drain records the events it refused in one events.refused Info record. Both kinds are counted in diagnostics().counters.eventsDropped.closed. Collector callback cleanup is awaited, so a collector callback that ignores cancellation can delay shutdown
Repeated and concurrent calls share the shutdown outcome. A pending connection call fails with ClientClosedError rather than cancellation. Established sockets get up to 5,000 ms to close gracefully, then are terminated and their close events awaited. Pending handshakes are terminated immediately, and forced termination can discard unsent data
Cleanup cannot be abandoned, and this method never exits the application or undoes remote writes. Create a new client to connect again
A failed cleanup step, such as a socket close that throws or an event handler or collector callback whose cancellation cleanup fails, is also logged at Error in the lifecycle category and counted in diagnostics().counters.cleanupFailures
shutdown(options?: ShutdownOptions): Effect<void>shutdown(
options?: ShutdownOptions
): Effect<void>Parameters
optionsoptional-
ShutdownOptions
- Returns
Effect<void>
Once shutdown starts, it disables interruption so the caller cannot abandon cleanup. Closing the client's creation Scope shuts down without a drain. Handler and collector finalizers must finish before closure, so work that disables interruption can delay shutdown
If an owned handler or collector callback calls shutdown, the client Scope performs shutdown and interrupts that invocation, which does not resume afterward. No expected error is returned. Invalid options are a defect carrying ConfigurationError, before shutdown starts. The drain time is measured with the Clock supplied when the client was created
Cleanup faults remain defects in the Cause, and an interruption of an owned worker stays a separate Cause reason
import type { Client } from "@neontechspace/fluxerly/effect"
export function stopGracefully(client: Client) {
// Let running handlers finish their replies for up to 10 seconds
return client.shutdown({ drainMs: 10_000 })
}
subscribe#
Receive future events of one type in receive order through a bounded buffer
No history is replayed and bulk events are not split into individual events. Enabled cache changes happen before delivery and remain independent of this subscription and its overflow. Overflow fails this subscription rather than silently dropping events. Options govern the source buffer only
subscribe<K extends keyof EventMap<Message>>(
event: K,
options?: EventBufferOptions
): Stream<EventMap<M>[K], EventOverflowError>subscribe<
K extends keyof EventMap<Message>
>(
event: K,
options?: EventBufferOptions
): Stream<
EventMap<M>[K],
EventOverflowError
>Parameters
event-
K optionsoptional-
EventBufferOptions
- Returns
Stream<EventMap<M>[K], EventOverflowError>Stream< EventMap<M>[K], EventOverflowError >- Fails with
Returns a Stream, and each execution owns a subscription released with the Stream's scope. Consumers choose stream concurrency and supervision. Registration misuse is a defect carrying ConfigurationError. A Stream run after shutdown began is not misuse: It ends at once without events and writes a Warn log record with code events
Registration happens when the Stream's first pull runs. Building the Stream, running Stream.toPull or forking its consumer does not confirm it, so an event sent right after can be missed. Before triggering an action, run a scoped on subscription or register a collector instead, as the example does
import { Deferred, Effect } from "effect"
import type { Client, Message } from "@neontechspace/fluxerly/effect"
export const announceAndWait = (client: Client, channelId: string, userId: string) =>
Effect.scoped(
Effect.gen(function* () {
const next = yield* Deferred.make<Message>()
// The subscription exists once on succeeds, so the send below cannot outrun it. It sees every message,
// including the bot's own prompt, so it keeps only the user's reply in this channel
yield* client.on("messageCreate", (message) =>
message.channelId === channelId && message.author.id === userId
? Deferred.succeed(next, message)
: Effect.void,
)
yield* client.messages.send(channelId, "Say something")
return yield* Deferred.await(next)
}),
)
use#
Add event middleware that runs around every later on handler invocation, for every event and subscription, including subscriptions registered before it, command routers and runBot handlers
Middleware runs in registration order, the first registered outermost, and each invocation keeps the middleware registered when it started. It receives the invocation's event name, payload, EventContext and subscription ID, and a next step that runs the rest of the chain and the handler. Not calling next skips the handler for that invocation
Calling next again returns the same completion without running the handler twice, and calling it after the middleware finished reports a ConfigurationError instead of running the handler. The handler slot stays occupied until the middleware and any next it started have finished
The next step completes whether or not the rest of the chain failed and never fails itself, so a handler failure is always reported. A failing middleware is reported like a failed handler, with the event name, subscription ID and message IDs, to the subscription's onError, else the client-level onError, else an Error log record
Middleware does not run for subscribe, waitFor, message collectors or reaction collectors. Registration misuse, such as a middleware that is not a function, fails like other registration misuse
use<E = never, R = never>(
middleware: EventMiddleware<M, E, R>
): Effect<MiddlewareRegistration, never, Scope | R>use<E = never, R = never>(
middleware:
EventMiddleware<M, E, R>
): Effect<
MiddlewareRegistration,
never,
Scope | R
>Parameters
middleware-
EventMiddleware<M, E, R>
- Returns
Effect<MiddlewareRegistration, never, Scope | R>Effect< MiddlewareRegistration, never, Scope | R >
The middleware receives the invocation and a next Effect, and runs with the services available when this Effect executes,
except that its Scope stays the one of the handler invocation. The next Effect restores the handler's own services.
Registration lasts until the returned registration's close or the closing of the executing Scope.
Registration misuse, including a closing client, is a defect carrying ConfigurationError or ClientClosedError
import { Clock, Effect } from "effect"
import type { Client } from "@neontechspace/fluxerly/effect"
export function timingMiddlewareExample(client: Client) {
return client.use((invocation, next) =>
Effect.gen(function* () {
const startedAt = yield* Clock.currentTimeMillis
yield* next
const durationMs = (yield* Clock.currentTimeMillis) - startedAt
yield* Effect.logDebug(`${invocation.event} took ${durationMs} ms`)
}),
)
}
waitFor #
Wait for the first future event of one type that passes the supplied synchronous filter
Observation starts without connecting the client, reading history or cache, or making a remote request
waitFor<K extends keyof EventMap<Message>>(
event: K,
options?: EventWaitOptions<K, M>
): Effect<EventMap<M>[K], EventWaitFailure>waitFor<
K extends keyof EventMap<Message>
>(
event: K,
options?: EventWaitOptions<K, M>
): Effect<
EventMap<M>[K], EventWaitFailure
>The default timeout is 30,000 ms from registration.
A timeout, a throwing filter or a non-boolean filter result fails with EventWaitError.
A thrown value is kept as the error's cause and can contain application data, while a non-boolean result is not kept.
Buffer limits match subscribe.
Reconnection can miss events and does not reset the deadline.
Overflow, invalid configuration and client closure remain distinct failures
Completion or cancellation releases the queue, filter, timer and subscription without shutting down the client. This returns an event, not a registration handle. Use subscribe, a scoped on subscription or a collector to confirm registration before triggering an action
Parameters
event-
K optionsoptional-
EventWaitOptions<K, M>
- Returns
Effect<EventMap<M>[K], EventWaitFailure>Effect< EventMap<M>[K], EventWaitFailure >- Fails with
Each execution owns a separate bounded subscription in its executing fiber, with no separate SDK runtime. Creating or forking this Effect is not a registration barrier, and neither is building or pulling a subscribe Stream in another fiber, so confirm registration with a scoped on subscription or a collector. SDK and cleanup defects remain in the Cause
import type { Client } from "@neontechspace/fluxerly/effect"
export function eventWaitExample(client: Client, channelId: string, userId: string) {
return client.waitFor("typingStart", {
filter: event => event.channelId === channelId && event.userId === userId,
timeoutMs: 10_000,
})
}
waitForClose #
Wait for this client's final shutdown or permanent connection failure, without starting or owning a connection
Recovery keeps the wait pending. An expected connect failure before initial readiness also leaves this wait pending, because the client can connect again. Multiple or later waiters receive the same retained outcome. Normal shutdown succeeds with no value, and a permanent connection failure fails with ConnectionFailure. After initial readiness, required-shard failure in a multi-shard plan is retained as ShardConnectionError. Cancelling this wait affects only this waiter, not the client or other waiters
waitForClose(): Effect<void, ConnectionFailure>waitForClose(): Effect<
void, ConnectionFailure
>- Returns
Effect<void, ConnectionFailure>- Fails with
Closing the client's creation Scope still shuts down the client. Background and cleanup faults remain in the Cause, alongside any expected failure, which Effect then reports as a defect that keeps its value