Reading and Caching Data

History reads, opt-in caches and collectors, with cached copies kept apart from fresh fetches

A database of stacked glass discs holding starry water

History Without the Gateway

Reading earlier messages is a request to Fluxer, so it works without the gateway connection. iterateHistory reads newest first and asks for the next page only when the loop needs it, within the maxItems bound you set

When the text is known, search asks Fluxer's search index for a page of results. While Fluxer is still preparing that index, the helper returns undefined instead of an empty answer that looks final

History in the Docs
TypeScript
import type { Client, Message } from "@neontechspace/fluxerly"export async function findRecentMessage(    client: Client,    channelId: string,    matches: (message: Message) => boolean,) {    for await (const item of client.messages.iterateHistory(channelId, { maxItems: 200 })) {        if (item.isErr()) return item        if (matches(item.value)) return item    }    return undefined}
TypeScript
import type { Client } from "@neontechspace/fluxerly"export async function searchChannel(client: Client, channelId: string, text: string) {    const result = await client.messages.search({ channelId }, { content: text, limit: 10 })    if (result.isErr()) return result    if (result.value.indexing) return undefined    return result.value.messages}

Caches You Size and Choose

A cache is a local copy of Fluxer data, and the client keeps none unless asked. Each category you turn on is bounded, by default to 1,000 entries and 4 MiB (8 MiB for messages), and a maxAgeMs function decides per entry. The value null keeps it and 0 skips it

Community data stays when a session resumes, except cached threads, which are cleared. A new session clears community data. Messages that a lost connection could have changed are cleared either way

Cache Settings in the Docs
A stack of cached discs
TypeScript
import { createClient } from "@neontechspace/fluxerly"export function createCachedClient(token: string, watched: ReadonlySet<string>) {    return createClient({        token,        cache: {            guilds: true,            // Keep channels of watched communities with no age limit, skip all others            channels: { maxAgeMs: (channel) => (watched.has(channel.guildId) ? null : 0) },            messages: { maxEntries: 500, maxBytes: 2_000_000, maxAgeMs: 60_000 },        },    })}

Local Get, Remote Fetch

messages.get returns the copy the client already holds, without a request, or undefined. fetch asks Fluxer for the current state. The two stay separate calls, so the code always shows which one it uses

A cached copy is a snapshot of what the client last saw, and Fluxer still decides what a request may do. After a clear, a read already in flight cannot put the removed entry back

Fluxer still checks every request, whatever the cache holds

Use the Cache, Then Fetch
TypeScript
import { orThrow, type Client, type MessageReference } from "@neontechspace/fluxerly"export async function readCachedOrFetch(client: Client, message: MessageReference) {    const cached = client.messages.get(message)    if (cached !== undefined) return cached    return orThrow(await client.messages.fetch(message, { timeoutMs: 5_000 }))}

Ask, Then Collect the Answer

waitFor returns the next matching event, or a failed Result if none arrives within timeoutMs, 30 seconds by default. A collector gathers matching messages or reactions for a limited time, and created before the question is sent, it cannot miss a fast reply

await using closes the collector when the helper returns or throws, and waits for its cleanup. A lost connection fails the collection instead of returning answers that may be incomplete

Events and Collectors
TypeScript
import { orThrow, type Client } from "@neontechspace/fluxerly"export async function askPreferredName(client: Client, channelId: string, userId: string) {    await using collector = client.messages.collect(channelId, {        filter: (message) => message.author.id === userId && !message.author.isBot,        maxMessages: 1,        timeoutMs: 30_000,    })    orThrow(await client.messages.send(channelId, "What name should the bot use?"))    const answer = orThrow(await collector.result())    return answer.messages[0]?.content}
TypeScript
import { orThrow, type Client, type MessageReference } from "@neontechspace/fluxerly"export async function waitForConfirmation(client: Client, message: MessageReference, userId: string) {    await using collector = client.messages.collectReactions(message, {        emoji: "✅",        filter: (reaction) => reaction.userId === userId,        maxReactions: 1,        timeoutMs: 20_000,    })    const result = orThrow(await collector.result())    return result.reactions.length > 0}

Invalid Events Are Logged and Skipped

When Fluxer sends a known event that fails validation, the SDK logs gateway.dispatchRejected with the event type and the failing field, skips the event and keeps the session running

It also clears the cache entries that event could have changed. A bot that would rather stop can set gateway.onMalformedDispatch to "terminate"

Limits and Retry Rules
TypeScript
import type { Client } from "@neontechspace/fluxerly"// Gateway events that failed validation. Each was logged as gateway.dispatchRejected// and skipped, the cache entries it could have changed were cleared, and the session// kept runningexport function skippedGatewayEvents(client: Client) {    return client.diagnostics().counters.eventsDropped.malformed}