Channel, Guild, Message, Send, User, Count, Bot, Message, Attachment, OAuth, Instance
Changelog
What changed in each release of the Fluxerly.js SDK, newest first
1000.0.0
Major Changes
-
5e98527: Require Effect 4.1.0 or newer. The
effectpeer range is now^4.1.0, so every supported Effect release reports combined failures the same way. Since Effect 4.0.6, an expected failure that occurs together with a defect, such as a cleanup fault, is reported as a defect that keeps the failure's value. In the default API, such anSdklists the original error as aDefect Defectreason instead of aFailurereason, while its cause, message and code still come from the actual fault, such as the cleanup failure. In the native API, the Cause holds the original error as aDienext to the cleanup defect, socatchno longer recovers from it. The original error is still the public one, such asTag MessageorOperation Error Connection, never an internal REST or gateway failureError Migration: Update Effect to 4.1.0 or a later Effect 4 release. Code that read the
Failurereason of anSdkto find the original error should also look at itsDefect Defectreasons -
2c9dfde: Breaking change for code that builds
Shardobjects by hand, such as test doubles ofState client: The new.shards sessionfield is required. AddEpoch sessionto such objects. The same applies to code that buildsEpoch: 0 Eventobjects by hand, such as tests that call handlers or middleware directly, which must now setContext sessionandEpoch receipt, and to code that buildsBotobjects by hand to callEvent Context runhandlers directly, which must now setBot context -
321d8bb: Breaking changes in
commands: A single quote is now an apostrophe unless it starts a word, and its rejections carry the command.parse Quoted name. Double quotes and backslash escapes are unchanged, and the rejections for an unclosed quote and a trailing backslash change only as described below. A single quote opens a quoted span only at the start of the text, after whitespace or directly after a span that just closed. Inside a single-quoted span, a single quote ends the span unless the next character is a letter or digit, so the end of the text, whitespace, quotes and punctuation all end it. Every other single quote is literal.Inputs that now parse differently, shown as the words each gives (arguments after
!say):don'tandit's Ada's: Nowdon't, thenit'sandAda's. Before, the apostrophe opened a span, sodon'twas rejected as an unclosed single quote andit's Ada'sgave the one wordits Adasa'b c'andkey='a b': Nowa'bwithc', andkey='awithb'. Before,ab candkey=a ba''b,a'b'canda': Now unchanged asa''b,a'b'canda'. Before,ab,abcand an unclosed single quote'it's fine','don't'and'x'y': Nowit's fine,don'tandx'y. Before, all three were rejected as an unclosed single quote'the dogs' bowl': Nowthe dogsandbowl'. Before, rejected as an unclosed single quote'a'b,'a b'c,''a,'a'1and'日本'語: Now rejected as an unclosed single quote, because the second quote is followed by a letter or digit and so stays inside the span. Before,ab,a bc,a,a1and日本語a' 'bandx'y 'z: Now rejected as an unclosed single quote, because the first apostrophe no longer opens a span and the later quote does. Before,a bandxy z
Inputs that parse as before include
'Yes', 'No'(Yes,andNo),'a'.,'a'-b,'a' 'b','a''b'(ab),don\'tand every input without a single quote.Rejections that
commandsreturns for an unclosed quote and a trailing backslash now also carry.parse Quoted name, the command name read before the mistake, so a reply can say which command it was for. A parse and a rejection can therefore both carryname, and a caller that told them apart with"name" in resultmust use"reason" in resultinstead.The
reasonof acommandsrejection for an unclosed single quote now explains the common cause when the open span held a single quote that a letter or digit followed, as in.parse Quoted 'a'b. The text then also says that a single quote before a letter or digit may have been meant to close the span, and suggests a space after that quote or double quotes. Thereasonfor a single quote without that cause, for a double quote and for a trailing backslash is unchanged, and the rejection keeps itsname. Code that comparedreasonwith the exact text of the first form must match the new text, or the shared startA single quote was opened but never closed.To migrate, remove workarounds such as
don\'t, which still works but is no longer needed. Wrap values that follow text in double quotes, such askey="a b", and end a single-quoted span before whitespace or punctuation, not before a letter or digit, or use double quotes, which may touch text, such as"a"b. A word that begins with an apostrophe, such as'twas, still opens a span, so write it as\'twas. -
0209093: Public type names no longer carry
DefaultorNativeprefixes, and the per-resource request option types becomeRequestandOptions AuditedRequest Options Each entry point exports a type that differs between the APIs under one name with its own shape, as
Clientalready did. In@neontechspace/,fluxerly RequesthasOptions signalandtimeout, and inMs @neontechspace/it hasfluxerly/ effect timeout.Ms AuditedaddsRequest Options auditin both. Notes that the removed option types carried, such as the per-page deadline ofReason messages, now sit on the methods that use them. The.iterate Search replyhelpers of default command and event contexts takeOmit<Request, because they apply the handler's signal. Runtime behavior is unchangedOptions, "signal"> Migration:
- Both
- Old name
Operation Options Operation Options Operation Options Options Operation Options Operation Options Application Operation Options Search Options Refresh Options Operation Options Resolve Options - New name
- Both
- Old name
Channel,Audit Operation Options Guild,Audit Operation Options Message,Audit Operation Options Moderation,Options WebhookOperation Options - New name
- Default
- Old name
Defaultplus any name in the two rows above - New name
- Default
- Old name
Default<Name>, such asDefault,Can Manage Options Default,Collector Options Default,Rest Request Default,Prefix Command Context Default,Supervisor DefaultorGuards DefaultCommands - New name
<Name> - Effect
- Old name
Native<Name>, such asNative,Prefix Command Context Native,Supervisor Native,Guards NativeorCommands NativeCommand Requirements - New name
<Name>
In the default entry point, names such as
Can,Manage Options Collector,Options Prefix,Commands Options SupervisorandChild Options Restnow name the default shape, which includesRequest signalwhere the oldDefaulttype did. The Effect entry point no longer exports the default-APIPrefixandCommands Options Supervisorshapes, and those names now refer to its ownChild Options -
7733b23: Make request errors say what to change. Rejections carry a
hintfor missing permissions or access, naming what a message send or a moderation action needs, unknown IDs, rejected tokens, invalid form fields, refused direct messages, two-factor requirements, slowmode, resource limits and temporary Fluxer failures, with a fallback by HTTP status for 401, 403 and 404. A rejected credential gets a hint for the credential the request sent: The webhook ID and token for a webhook client, the OAuth client ID and secret for aninvalid_error or a 401 from a code exchange, refresh, revocation or introspection, and the user's access token for a 401 fromclient fetch,Identity fetchorGuilds fetch. Operation error messages lead with the failed operation and Fluxer's answer, for exampleConnections Message send failed: Fluxer reports that the bot lacks a required permission (, and explanations name Fluxer instead of "the provider"MISSING_ PERMISSIONS, HTTP 403) Keep Fluxer error codes that this SDK version does not describe yet. An uppercase code of up to 64 characters is recorded as
detailsand named in the message while.provider Code apistaysError null, and an unrecognized validation code stays inapiwith its field path and a generic explanation.Error .validation Errors OAuthandOperation Error Messagekeep unrecognized codes the same way. A response that does not match the expected shape names the failing field or check, such asCleanup Error typeorchannel, inMismatch detailsand logs a.response Field restWarn. A message cleanup that fails in a history read or batch request copies that step's.response Rejected details. A.response Field clientroute has no expected shape, so its unusable response is only returned in the Result. The.rest .request membersand.fetch Can Manage permissionschecks keep these facts and the failed step's error as.fetch causewhen they report a failure under their own operation name.Operationand theError Options MessageandError Messageconstructors acceptCleanup Error providerandCode response, and theField OAuthconstructor acceptsOperation Error providerCode Log HTTP
401and403rejections as arestWarn with the Fluxer code and hint, even when the application handles the Result. When an event handler or command fails with the rejection and no.rejected onhook receives the failure, the handler's failure record reports it instead, so one failed reply is logged once. Webhook clients and OAuth clients accept the sameError loggingoption as a client, so their rejected webhook tokens and OAuth client credentials are logged the same way. A rejected OAuth user access token concerns one user and is only returned. Like other Warn records, identical repeats within one minute are printed once by default, so a rejection that keeps happening shows about once a minute with a repeat count. Other client rejections are logged at Debug, andrestDebug records carry the Fluxer code in.request fields.api Error A
Configurationthrown by the defaultError createorClient runpoints at the caller's lineBot Migration:
Apiis nowValidation Error Detail .provider Code Api, so code that assigns it to anValidation Code | ( string & {}) Apimust narrow it first. Operation error message text changed, so match onValidation Code code,reasonandapirather than on messagesError -
abb5d3f: The coding agent guide moved from
consumer/toAGENTS.md agents/in the installed package. An application'sAGENTS.md AGENTS.mdsection from an earlierfluxerly agentsrun points at the old path, so runnpx --no fluxerly agents,pnpm exec fluxerly agentsorbunx --no-install fluxerly agentsagain after updatingThe README now links the documentation's
llms.txtfor agents that read web pages, and its Effect section describes the accepted Effect 4 range instead of an exact version -
abb5d3f: Add
Channel(5),Type .Announcement Guild,Announcement Channel GuildandText Channel Base Announcementto both APIs. Announcement channels now decode into their own shape instead ofChannel Create Guild, so exhaustive channel-type switches need an announcement caseUnknown Channel Allow
Channelto convert between text (0) and announcement (5), including a type-only patch. A text channel that follows an announcement channel cannot become one. Delete its follower webhooks first. Conversion to text queues asynchronous follower webhook removal, and aEdit .type guildevent replaces the cached channel with its new typeChannel Update -
66e4f2b: Audit-log reads and
guildevents no longer fail on an audit action this SDK version does not name.Audit Log Entry Create Auditis now aLog Entry .action Type numberthat keeps any action Fluxer records, including ones added later, so compare it withAuditvalues. Previously one such entry rejected its whole audit-log page and made the gateway event malformed.Log Actions AuditgainsLog Actions Thread(110),Create Thread(111) andUpdate Thread(112), which also work asDelete actionfiltersType -
7733b23: Ban durations are milliseconds, like member timeouts, command cooldowns and the
durationcommand argument. TheBanfields are nowInput duration, 0 for a permanent ban or 60,000 through 63,072,000,000 for a temporary one, andMs delete, 0 through 604,800,000. Both must be whole seconds and are sent to Fluxer in seconds. A rejected ban input names the failing field, such asMessages Ms input, and states its whole-second range.duration Ms Audit-log entries report the message deletion of a member ban in milliseconds too, so the
AuditfieldLog Options deleteis nowMessage Seconds delete, in both API styles and inMessages Ms guildeventsAudit Log Entry Create A
durationcommand argument acceptswhole, which rejects a value with a millisecond part such asSeconds: true 1m500msand adds "in whole seconds" to the rejection reply. Withwhole,Seconds: true min: 60_000andmax: 63_072_000_000, the argument's value can be passed todurationunchanged, and a moderator's out-of-range value gets the usage reply instead of a failed banMs Migration:
- Replace
durationwithSeconds: n durationandMs: n * 1000 deletewithMessage Seconds: n deletein ban inputMessages Ms: n * 1000 - Replace
optionswith.delete Message Seconds optionson audit-log entries, and divide by 1000 where seconds are still needed.delete Messages Ms
- Replace
-
7733b23: The command argument types that pick from a fixed list are renamed to
user,Choice channelandChoice role, next toChoice choice, so the namesuser,channelandroleno longer suggest a lookup in the message's community. They still select one of up to 100candidatesgiven at registration. The old names now fail registration withConfiguration, whose hint namesError { type: "id", mention }for any ID or mention,{ type: "member" }for a member of the message's community when the old type wasuser, and the new list formMigration: Replace
type: "user","channel"and"role"with"user,Choice" "channelandChoice" "rolein argument schemas, and in code that readsChoice" CommandArgument Metadata .type -
07d2b9d: A prefix command parser can now explain why it declined a message. The
commandsparser now returns a new.parse Quoted Prefixwith a fixedCommand Parse Rejection reasonfor a quote that never closes or a backslash at the end, where it used to returnundefinedand give no hint, and a customparsefunction can return the same{ reason }shape. The router'soncallback, which already handles unknown names and groups typed without a subcommand, receives the reason onUnmatched Commandin both entry points, and the DebugParser Rejected commandsrecord includes it. The router still sends no reply by itself, so a bot decides whether to turn the reason into one. A rejection may also carry.unmatched namein command-name syntax. A rejection with an empty or non-textreason, an invalidnameor other keys is reported like any malformed parser result. The commands guide now explains how to opt in to the quoted parser and how to reply to unmatched messages. A direct caller ofcommandsthat tests the result for truthiness or reads.parse Quoted argsmust now rule out a rejection first, such as with"reason" in result, because only a rejection hasreasonand a parse hasname,argsandrawArgs -
7733b23: Rename client members so each name states what it does and each concept uses one word across both entry points. Deleting a bot's own history now requires an explicit
{ confirm: true }option, checked by TypeScript and again at runtime, and a missing confirmation fails with reasoninputbefore any request. Subscriptions, collectors and state observers all end withclose(), and a collector'sresult()returns its final collection.MessagereportsError outcomewith the samenot,Dispatched rejectedandunknownvalues as other operation errors, so an API rejection is now distinguishable from a send that never startedMigration:
- Replace
guildswith.delete Mine( guild Id) guilds, and.delete Own Messages( guild Id, { confirm: true }) messageswith.delete Mine( channel Id) messages. The required.delete Own Messages( channel Id, { confirm: true }) confirmoption joins the existing timeout and signal options. The operation IDs change fromguildsand.delete Mine deletetoMine guildsand.delete Own Messages deleteOwn Messages - Call
members,.ban membersand.unban membersinstead of.fetch Bans guilds,.ban guildsand.unban guilds, with the same parameter order. Their operation IDs change from.fetch Bans guilds,.ban guildsand.unban guildsto.fetch Bans members,.ban membersand.unban members.fetch Bans - Rename
invitesand.fetch Channel invitesto.fetch Guild invitesand.fetch For Channel invites, and.fetch For Guild webhooksand.fetch Channel webhooksto.fetch Guild webhooksand.fetch For Channel webhooks. Their operation IDs change to the same new names, so code matching.fetch For Guild erroron.operation invites,.fetch Channel invites,.fetch Guild webhooksor.fetch Channel webhooksneeds updating.fetch Guild - Replace
applicationwith.fetch Current() application. The operation ID changes from.fetch() applicationto.fetch Current application.fetch - Replace
memberswith.fetch Hierarchy Check( target) members. The operation ID changes from.fetch Can Manage( target) membersto.fetch Hierarchy Check members.fetch Can Manage - Replace
memberswith.set Mute( target, true) members, and.set Mute( target, { muted: true }) memberswith.set Deaf( target, true) members. The exported.set Deaf( target, { deafened: true }) VoiceandMute Input Voicetypes describe these inputsDeafen Input - Call
close()instead ofsubscriptionon subscriptions, and instead of.unsubscribe() collectoron message and reaction collectors, in both entry points.stop() - Call a collector's
result()instead of itswait. The operation IDs change fromFor Close() collectorand.wait For Close reactiontoCollector .wait For Close collectorand.result reactionCollector .result - The default
clientreturns a.observe State( listener) Stateinstead of a function. CallObserver observerwhere the returned function was called.close() - A malformed target passed to
clientthrows.messages .get Messagewith operationOperation Error messagesinstead of.get get, matchingusersand.get channels, and an.get Sdkfrom that lookup also namesDefect messages. Code that compared a lookup failure's.get operationwith"get"compares it with"messagesinstead.get" - Replace
clientwith.events( event) client, which no longer shares a name with the.subscribe( event) runevents option. The operation ID changes fromBot eventstosubscribe - Read
Messageinstead ofError .outcome Message. ReplaceError .delivery "notchecks withSent" outcome !== "unknown", or distinguish"notfromDispatched" "rejected", and passoutcomeinstead ofdeliverywhen constructing the error
- Replace
-
6eaecbf: Expand received message context with immutable partial-user metadata, non-notifying referenced users, explicit-emoji classifications and one resolved reply level without hidden requests. The
referencedtype is nowMessage Referenced, not an address-onlyMessage Message. Hand-authored message fixtures must supply the resolved message shape or omit the optional field. Address-only operations can continue to useReference Message. Additional retained context counts toward configured cache and collector byte budgetsReference Add explicit attachment URL refresh and endpoint-specific audit reasons through both APIs. Refresh preserves exact signed strings and does not download attachments. OAuth preflight now rejects overlong or noncanonical opaque values rather than changing them and bounds supplied scope arrays to 256 entries before deduplication. Member-role replacement enforces provider signed-identifier bounds
Improve resource-aware refill pacing, unrelated user and DM cache reads, incremental cache byte accounting and concurrent member-search preflight without replaying ambiguous writes. Scope logical scheduling to the owning Effect clock while retaining separate host-safety watchdogs
Native owner-backed gateway, REST, collector, member-stream, cache and presence timing now follows the Clock supplied when the client is created. Applications overriding logical time must create the client under that Clock rather than replacing it only around a later operation. Ownerless utilities retain their documented executing-clock behavior
-
7733b23: The
Embedmethod takes its display setting as a named option, following the rule that settings are option objects rather than boolean positionals. The newBuilder .field Embedtype describes itField Options Migration: Replace
field(withname, value, true) field(, and drop aname, value, { inline: true }) falsethird argument. A leftover boolean or other non-object third argument throwsConfigurationwith a hint namingError { inline: true } -
c8c87b0:
Embedand.provider Embednow have their ownChild .provider Embedtype, with onlyProvider nameandurl, matching what Fluxer sends. They were typed asEmbed, whoseAuthor iconandUrl proxynever arrived on a provider.Icon Url Embedis exported from both entry points, and a provider's icon fields are no longer decodedProvider -
7733b23: Base every SDK error on the exported
Fluxerlywith a stableError code, an optional fixhint, frozendetails, a standardcauseandto. Errors now keep the application or transport failure that caused them. A network failure is kept as a cause that records only the transport error code, such asJSON() ECONNRESET, never the transport message. API validation failures include the sanitized field path with each error code. Messages andtooutput mask credential patterns, including application text quoted byJSON() ApplicationandError Sdk, whileDefect causekeeps the original value. Masking applies to error text,describeoutput and log records. It covers bare Fluxer bot tokens of the formError <application_, keeping the application ID. It also covers the value after an assignment to a key ending inid>.<secret> tokenorsecret, such asFLUXER_, the value after a quoted key such asBOT_ TOKEN= "bot, and a credential-shaped value after an unquoted key and a colon, so prose such asToken": Missing token: check the configurationstays readableAdd
describefor readable output with the code, hint, safe details and cause chain. It collapses stack frames inside the SDK and Effect into one line and prints no frames for expected SDK errors other thanError( error) ConfigurationandError Sdk. It also accepts an Effect Cause and describes each failure, defect and interruption, so native programs can passDefect exitdirectly. A thrown value that is not an Error is named.cause Non-Error value (and similar. Addstring) errorswithapi,Code isandRetryable match. Theerrorshelper returns the recognized Fluxer rejection category of an operation error, such as.api Code( error) "missingorPermissions" "two, and undefined for every other value, soFactor Required" errorsworks on any error of an operation's Result without checking its.api Code( result .error) === "missing Permissions" _tagfirst. A read that failed after it was sent recordsdetails, so.read errorstreats it as safe to repeat, unlike a write with an unknown outcome. Add.is Retryable Applicationfor failures raised by application callbacks such as aError runBot setuporcommandscallback or akeeptaskTyping Migration:
Message,Operation Error Channel,Operation Error Guild,Operation Error User,Operation Error Webhook,Operation Error Bot,Application Operation Error Attachment,Refresh Error Message,Cleanup Error MessageandError OAuthnow take one options object, described byOperation Error Operationfor the resource operation errors. ReplaceError Options new AttachmentwithDownload Error( reason, status, input Validation) new Attachment.Download Error( { reason, status, input Validation, cause }) Count,Operation Error Member,Chunk Error PaginationandError Presencechange the same way, takingError { operation, reason, input,Validation, cause } { reason, retry,After Ms, input Validation, cause } { operation, reason, inputandValidation, cause } { reason, input.Validation, cause } Presenceno longer defaults its reason toError input, and omitted status, retry and input detail values still default to null.Configuration,Error Connection,Error Helper,Error Asset,Url Error Collector,Error Event,Wait Error SupervisorandError Supervisoraccept a trailing options object withChild Error cause.Configurationalso acceptsError hintthere, andConnectionacceptsError hintanddetails -
7733b23: Report every failure that has no returned result, including throwing event handlers, failed commands, collector callbacks and filters, cleanup progress callbacks, message cache callbacks, state observers and overflowing subscriptions. Each failure goes to the subscription's
on, then the client's newError onoption, or is logged at Error with its full message, stack and cause chain. AError Failurecarries the original thrown value unchanged, its kind, and the event, command, subscription and message IDs involved, andReport describe()returns readable text. Native reports also keep the Effect causeA failure only queues its report. Each client, subscription and command router hook receives reports one at a time in order through its own bounded queue, so a slow hook never holds up handlers. Later reports are logged and counted instead, and a failing hook is logged together with the original failure without being retried. Shutdown interrupts running hooks without waiting for one that never finishes, and logs the interrupted and queued reports. Command routers now report failures with the command name, including failed
oncallbacks and parsers, and rejected or unmatched commands are logged at Debug and countedUnmatched Migration:
Handleris replaced byError Report Failure, and the message cacheReport onoption andError Cacheare removed in favour of the client'sPolicy Error Report on. A message cleanupError oncallback that throws, rejects or returns an Err is now reported toProgress onwith kindError progressinstead of being ignored, and cleanup continues. The callback stays synchronous, so a returned Effect is not run and is reported as aType.Error Sdknow keeps each fault's original value inDefect reasonsand its first fault ascause. A fault from an application callback, or a throw while the SDK reads caller-supplied options or input, such as a property getter or a caller AbortSignal'sabortedgetter or listener methods, has originapplicationand codeapplication. Other faults, including faults in SDK work around those reads, have code.defect sdk.defect -
7733b23: Invalid data from the Fluxer gateway, such as a frame that is not JSON, a second HELLO or READY, or a sequence that goes backwards, no longer ends the client at once. The shard logs the failure with
fieldsset to.reason protocoland reconnects with a new session. Three such failures in a row without a healthy connection between them end the shard with a protocolConnectionas before. Oversized frames, invalid UTF-8, binary frames andError gateway: { onstill end the shard without retryingMalformed Dispatch: "terminate" } Migration: A single invalid gateway message no longer ends
client, because the shard reconnects. The protocol.run Connectionnow arrives only after three such failures in a rowError -
7733b23: The
runfunction lets running work finish before it stops, in both entry points. When itsBot signalaborts, or SIGINT or SIGTERM arrives withprocess, the bot stops accepting new events and gives running handlers and commands, events already waiting for them and their REST requests up to 5,000 ms to finish. Then it shuts down as before and cancels whatever is left. The newSignals: true drainoption changes that time. A stop caused by a failure still shuts down at once. Sending the same process signal again ends the process at once with the default Node.js behaviorMs Add
ShutdownwithOptions draintoMs clientin both entry points for the same drain outside.shutdown( options) run. Without options,Bot shutdown()still cancels running handlers at once. During a drain the connection state stays as it was, new subscriptions and event waits start closed, and handlers can still send requests. The drain ends as soon as no work is left, logginglifecycle. When the time runs out, a.drained lifecycleWarn record names how many handlers, events and requests were cut off. A.drain Timed Out lifecycleInfo record marks the start. An invalid.draining drainthrowsMs Configurationin the default API and dies with it in the Effect APIError Supervised children drain the same way when the supervisor stops them, for up to the supervisor's
shutdownminus one second, which keeps that second for closing the connection. A child whose client stopped on its own is not drainedTimeout Ms Migration: To keep cancelling running handlers as soon as the bot is asked to stop, pass
draintoMs: 0 runBot -
7733b23: Cache entries for communities, which Fluxer calls guilds, and for members, roles, emojis, stickers and channels now survive a successful Resume, because Fluxer replays every missed event on Resume or refuses the session. They are cleared when a shard has to start a new session. Users, direct messages and messages are still cleared on every lost connection
A
guildevent now fills the enabled role, emoji, sticker and channel caches from its snapshot and replaces that community's earlier entries, so a new session rebuilds them without REST requests. Members are not replaced, because the snapshot lists only a few. With the member cache enabled, the ones it lists are stored beside the othersCreate Migration: Code that relied on community caches being empty after a reconnect should call
guildsand the other fetch methods when it needs current state, since a cached entry now stays across a Resume.fetch -
7733b23: Skip a known gateway event that fails validation instead of ending the client. The SDK logs
gatewaywith the event type and failing field path, clears cache entries the event could have changed and counts it. Set.dispatch Rejected gateway: { onto end the shard with a protocolMalformed Dispatch: "terminate" } ConnectioninsteadError Add an
overflowhandler option withstop,dropandOldest droppolicies. Every dropped event is logged at Warn and counted inNewest diagnostics(). Handlers registered with.counters .events Dropped clientdrop the oldest waiting event by default when their queue is full, as.on runhandlers and command routers do, and the handler keeps running. An unknown event name fails withBot Configurationwhose hint suggests the closest valid name when one is similarError Migration: A full
clientqueue no longer ends the subscription. To keep that behavior, where.on waitreturnsFor Close Event, passOverflow Error { overflow: "stop" }toclient. The.on subscribestreams, event waits and collectors still end on overflow. Event handlers passed torunnow runBot messageup to eight at a time and drop the oldest waiting event when their queue is full instead of stopping the bot. PassCreate { handler, concurrency, overflow, maxinstead of a function to change this per event. Command routers attached without options, or with undefined settings, also run up to eight commands at a time and drop the oldest waiting messagePending Messages, max Pending Bytes, on Error } -
abb5d3f: Received
messagenow has its own type,.message Reference Message, separate from theContext Reference Messagethat message operations take in both APIs. ItsReference channelis required andId idis optional. Channel-follow notices, which have no message ID, now decode in events and REST reads, including minimal message field selections. A present but malformed ID still failsThis is a breaking type change: Check that the received context has an ID and build a
Messagefrom both fields before passing it to fetch, reply or another message operationReference Add
Messageand the received-only flagsType .Channel Follow Add Message,Flags .Crossposted MessageandFlags .Is Crosspost Message. Fluxer sets these publishing flags itself. Sending and editing continue to accept onlyFlags .Source Message Deleted SuppressandEmbeds Suppress, with other flags rejected before sending the requestNotifications -
2d263aa: Fluxerly needs Node.js 24.15 or newer, up from 24.11, because that is the oldest Node 24 release npm 12 supports
-
7733b23: Each helper has one public name, and the role hierarchy helpers join the other pure helpers in a namespace:
- The top-level
createexport is removed. UsePkce oauth, which is unchanged.create Pkce - The
hierarchynamespace replacescan,Manage Hierarchy compareandHierarchy iswithAbove In Hierarchy hierarchy,.can Manage hierarchyand.compare hierarchy. The.is Above Hierarchytype describes itHelpers
Migration: Replace
createwithPkce() oauth, and each loose hierarchy function with its.create Pkce() hierarchymethod - The top-level
-
7733b23: Reject misspelled and unsupported top-level option keys in
create,Client runandBot supervisorin both entry points with.create Configuration, whose hint names the closest supported key, such asError eventsforevent. TheclientofOptions supervisorare checked the same way, where an unknown key was silently dropped before, so the child client ran without it. Unsupported keys in the nested.child .run logging,cache,cache,.messages cache.<kind>,connection,connection,.recovery uploads,gateway,sharding,instanceand supervisorrestartsettings are named in the message with the same suggestion, and a removed logging setting names its replacementNormalize the configured token: Surrounding whitespace and one pair of matching quotes, which
.envfiles often add, are removed. A token that starts with aBotorBearerscheme is rejected withConfiguration, since the SDK adds theError Botscheme itself. Theauthhint suggests checking the configured value before regenerating the token.rejected Migration: Remove option keys that
create,Client run,Bot supervisoror the.create clientofOptions supervisordo not support, including unknown nested keys such as a.child .run connectionkey other thanstartup,Timeout Ms maxandStartup Attempts recovery, and pass the bare token without aBotorBearerprefix -
abb5d3f: Remove the retired phone-verification level. The
Guildconstant is gone and guild edits reject level 4 before the request. Level 4 received from older instances decodes asVerification Levels .Very High High(3), matching Fluxer. Other out-of-range levels still fail decoding. TheApitype dropsProvider Code GUILD_, and an unrecognized code stays inPHONE_ VERIFICATION_ REQUIRED details.provider Code -
7733b23: Return plain values from the local helpers in both entry points, and throw their existing error for invalid input, which indicates a programming mistake. This covers
format,snowflakes,permission,Bits colors,text,.split links,assets, the role hierarchy helpers inhierarchyand theassetsandlinkshelpers returned byclient. Invalid input throws the same.instance .resolve() Helper,Error AssetorUrl Error Guildwith the sameOperation Error operationandreasonthat the Result or Effect previously carried. In the native API such a throw inside Effect code becomes a defect rather than a typed failureAdd
format,.try Parse Mention format,.try Parse Timestamp format,.try Parse Custom Emoji snowflakesand.try Parse colorsfor text received from users. They apply the same rules as their plain counterparts without throwing, returning a Result in the default API and an Effect that fails with.try Parse Helperin the native APIError Migration:
- In the default API, helpers such as
formatnow return plain values directly, so drop.user Mention( id) is,Ok() valueand_unsafehandling from helper calls, and catchUnwrap() Helperonly where invalid input is possibleError - In the native API, helpers are no longer Effects, so drop
yield*andEffectaround calls such as.run Promise format,.user Mention( id) links,.installation( id) assets,.display Avatar( user) hierarchyand.can Manage( input) resolved.links .channel( target) - Parse untrusted text with
format,.try Parse Mention( text) format,.try Parse Timestamp( text) format,.try Parse Custom Emoji( text) snowflakesor.try Parse( text) colorsinstead of.try Parse( input) parse,Mention parse,Timestamp parse,Custom Emoji snowflakesor.parse colors, which now throw.parse - Native code that deferred validation by building a helper Effect before its input changed now reads the input when the helper is called
- In the default API, helpers such as
-
7733b23: Make public types describe what Fluxer actually sends and what builders accept.
Guildis a union ofChannel Guild,Text Channel Guild,Announcement Channel Guild,Voice Channel Guild,Category Channel Guild,Announcement Thread Channel Guild,Public Thread Channel Guild,Private Thread Channel Guild,Forum Channel Guild,Media Channel GuildandLink Channel Guild. ComparingUnknown Channel channelwith a.type Channelconstant narrows it to exactly that shape, soType if (yields achannel .type === Channel Type .Voice) Guild. A channel type this SDK version does not know arrives as aVoice Channel GuildwhoseUnknown Channel typeis"unknown"and whoserawholds the number Fluxer sent, so aType switchontypethat also handles"unknown"is exhaustive.Presenceis anUpdate .status Observed, and an unrecognized status from Fluxer becomesPresence Status "unknown"instead of an arbitrary string.Embedaccepts anyBuilder .color Color, such asInput "#ff8800"or an RGB tuple, andEmbedaccepts aBuilder .timestamp Date, epoch milliseconds or an ISO string. An invalid color, or an invalidDateor number, throwsHelperwith operationError embedor.color embed. The type error from calling.timestamp build()on an emptyMessagenow names the missing body, through the exportedBuilder Missingtype. Default clients, OAuth clients, webhook clients, subscriptions and collectors supportMessage Body await using, which shuts a client down or closes a subscription or collector and waits for its cleanupMigration:
- Narrow a
GuildonChannel type, for example withChannel, before readingType .Voice bitrate,useror other fields that belong to one channel typeLimit - Handle channel types outside
Channelby checkingType channeland reading the Fluxer number from.type === "unknown" channel. A numeric.raw Type typeno longer carries an unknown type - Compare
Presenceagainst the listed values and handleUpdate .status "unknown"instead of Fluxer strings outside that list Embedconverts its input, so pass the color directly rather than converting it first. A number outside the 24-bit range now throwsBuilder .color( value) Embedconverts aBuilder .timestamp( value) Dateor number withto. An invalidISO String() Dateor number now throws instead of producing an invalid embed
- Narrow a
-
7733b23: Public types now use one name per concept, and API rejection details are readable in the reference, in both entry points
MemberandSearch Hit .global Name OAuthare nowIdentity .global Name display, the nameName UserandMessagealready use for the account-wide display name. TheUser displayhelper therefore reads it from member-search hits and OAuth identities too.name - The deprecated
Messagealias is removed.Mention Messageis the only name for the partial-user shape in mentions and referencesUser ApiandError Detail Apiare plain interfaces. The newValidation Error Detail Api,Error Code ApiandProvider Code Apiunions list every recognized category and server code to branch on. TheValidation Code explanationfields are typedstring, andvalidationis present only whenErrors codeisinvalidForm Body - The
@neontechspace/entry point no longer exports the ten default-API option typesfluxerly/ effect Default,Member Chunk Options Default,Count Operation Options Default,Bot Application Operation Options Default,Expression Delete Options Default,User Operation Options Default,Webhook Operation Options Default,Attachment Download Options Default,Attachment Refresh Options DefaultandAttachment Stream Options Default. No Effect member accepts themMessage Cleanup Options
Migration:
- Read
displayinstead ofName globalonName MemberandSearch Hit OAuthIdentity - Replace
MessagewithMention MessageUser - Use
Api,Error Code ApiorProvider Code Apito branch on codes. Code that comparedValidation Code explanationagainst literal types compares strings instead - Effect code that imported one of these types imports the default-API type from
@neontechspace/under its name without thefluxerly Defaultprefix, orRequestorOptions Auditedfor the operation option types, or uses the Effect option type of the same operationRequest Options
-
7733b23: Return a Result or a failing Effect only where a failure can happen at runtime, such as a request, a gateway command, an event wait or a collector's outcome. Local setup returns its value directly: Creating clients, registering handlers, creating command routers and reading caches. Misuse of that setup, such as invalid options, a missing token or an unknown event name, throws
Configurationin the default API and is a defect in the native API, so a misconfigured bot fails at startup instead of returning an easily ignored Err. An Err returned or resolved by an application callback, including event and command handlers, guards, middleware, collector callbacks,Error onhooks, state observers and supervisor configuration, is now reported like a throw of its errorError The default API exports
or, which returns a Result's value or throws its error and also accepts a Promise of a Result, and re-exports theThrow ResultandResulttypes. Callback return types acceptAsync unknown, and handler, collector and supervisor signals are typed asAbort. A missing or blankSignal tokenis accepted by the types asstring | undefinedand throwsConfigurationwith a hint that the environment variable holding it is probably unsetError Migration:
- Default
create,Client( options) create,Webhook Client( options) oauthand.create( config) supervisorreturn the client or supervisor directly. Drop.create( options) is,Err() valueand_unsafehandling and catchUnwrap() Configurationonly where options come from untrusted inputError - Native
create,Client create,Webhook Client oauthand.create supervisorno longer fail with.create Configuration. Invalid options become a defect, so handlers for that typed failure can be removedError - Default
clientreturns the.on( event, handler) Subscription,clientreturns the.subscribe( event) Event, andSubscription messagesand.collect( ...) messagesreturn the collector directly. A gateway that is not ready or an aborted signal now appears in the collector's.collect Reactions( ...) result()instead of the creation result. Nativeon,collectandcollectno longer fail, and a nativeReactions subscribestream fails only withEventOverflow Error - Cache lookups such as
clientreturn the cached value or.messages .get( reference) undefineddirectly, and returnundefinedafter shutdown instead ofClient. A malformed ID throws the namespace's operation error with reasonClosed Error inputin the default API and is a defect in the native API. Native lookups otherwise return an Effect that never fails - The default
clientreturns the array directly, and native.cache .entries( kind) cachenever fails.entries - The
clientmethod returns the.permissions .calculate( input) bigintdirectly in both entry points and throwsGuildwith reasonOperation Error inputfor inconsistent data - Command routers return values directly in both entry points. The
commands,.create register,register,Many register,Group helpandcommandscalls throw.memory Cooldowns() Configurationfor invalid definitions, and the defaultError attachreturns theSubscription. Dropyield*in front of those calls in native code, whereattachremains an Effect - A default cooldown store's
claimreturns the claim or a Promise of it rather than a Result - Callbacks that returned an Err to signal an ignored outcome now report a failure. Return nothing, or handle the Result inside the callback, when the failure is expected
- The
RegistrationandError Collectorexports are removed, because registration misuse now throwsRegistration Error Configurationand a registration during shutdown returns a closed handleError - TypeScript consumers that read handler signals or use
await usingneedAbortandSignal Asyncfrom the Node.js types, for exampleDisposable "types": ["node"]
- Default
-
7733b23: Command
idarguments now accept only IDs in the 64-bit ID range, in both entry points. A valid ID is a nonzero decimal without leading zeroes, up to9223372036854775807. The same limit applies to IDs inside the accepted mention forms. The value0and longer or larger values are now rejected asInvalidbefore the command runs. Commands that relied on0or out-of-range tokens reaching the handler need acustomargument instead -
7733b23: Command cooldowns no longer turn users away because a memory store is full, and commands can be hidden from help and suggestions, in both entry points
- A memory cooldown store never refuses a new key. When it is full, it removes expired keys first, then the reservation that expires soonest, which lets that key run again early. This applies to
commandsand to each router's own store.memory Cooldowns() - The default limit rises from 1,024 to 10,000 keys, and the router option
cooldowns: { maxsizes a router's own store, for exampleEntries } commands.create( { prefix: "!", cooldowns: { max Entries: 50_000 } }) - Commands and groups accept
hidden: true. Generated help leaves them out, along with everything inside a hidden group, and selecting a hidden group inhelpthrowsConfigurationas for a missing one. Hidden commands still run when invoked, so guard the ones that need itError
Migration:
- The
Cooldownclaim and theCapacity Commandrejection are removed, because a built-in store no longer refuses a key. A custom store returns onlyCooldown Capacity CooldownorAcquired Cooldown, and any other claim fails the command withActive Configuration. Code that handled either removed tag can drop that branchError - Code that relied on the old limit of 1,024 keys, for example to cap memory, passes
maxtoEntries commandsor the router's.memory Cooldowns cooldownsoption - Owner-only and other private commands that must not appear in help set
hidden: trueinstead of filtering them in everyhelpcall'sinclude
- A memory cooldown store never refuses a new key. When it is full, it removes expired keys first, then the reservation that expires soonest, which lets that key run again early. This applies to
-
7733b23: Registering a subscription or collector after client shutdown has begun no longer fails with
Clientat registration, in both entry points. A handler that is still running when shutdown starts can register, which is a runtime race rather than misuseClosed Error - A
clientor.on clientregistration, including a command router's.subscribe attach, returns an already-closed subscription. Its handler never runs,waitsucceeds and the defaultFor Close nextreturnsOk(. The nativenull) subscribeStream ends without events. Each such registration writes a Warn log record with codeevents.registered After Shutdown - A
messagesor.collect messagesregistration returns a collector whose.collect Reactions resultfails withClient, as it does for a collector already running when shutdown startsClosed Error - Invalid arguments are misuse and throw or die with
Configuration, whatever the client state. Registering middleware withError clienton a closing client throws or dies with.use ClientClosed Error
Migration:
- Code that handled
Clientfrom these registrations reads the handle instead. CheckClosed Error client, or read the collector's.state resultforClientClosed Error
- A
-
7733b23: Configure a whole bot with one
runoptions object in both entry points. ABot commandsoption routes prefix commands,setupruns after every handler is registered and before the connection starts, andonandError processsit beside them. TheSignals runfunction checks every option, including event names, event delivery settings and client settings, before any client, process signal listener or request exists, even when theBot signalis already aborted. Invalid configuration other than a missing or blanktokenthrowsConfigurationat the call, or is a defect in the native API. It then creates the client and registers every event and command synchronously. An abortedError signalwith valid options returns success without creating a client. A failedsetupor an application error thrown by acommandsregister callback is an application failure, reported asApplicationwith the original value asError cause. In the default API, asetupcallback that throws, rejects or returns an Err givessource"run, and in the native API a failedBot setup" setupEffect gives the same. A register callback that throws givessource"runin both APIs. A failedBot commands" setupstops the bot before connecting and shuts down its client, and a failed register callback stops it before any client exists. Misuse inside the register callback isConfiguration, and anError Sdkor a native defect keeps its original causeDefect Commands gain features for common bot needs:
- The
guardoption accepts one guard or an array, and a guard can return{ deny: "reason" }. The exportedguardsprovideguild,Only() dm,Only() ownerandOnly( ids) requirePermissions( names) - The
guildguard allows a message sent in a community, which Fluxer calls a guild, and denies every other message without a request. TheOnly() dmguard allows only a confirmed one-to-one or group conversation: It checks the direct-message cache, then the channel cache, and otherwise reads the channel once withOnly() direct, which also fills an enabled direct-message cache. A community channel is denied, and any other failed read fails the command and is reported with the command name. In the Effect API, its error type isMessages .fetch UserOperation Failure - The
requireguard reads cached community, member, role and channel data first. It reads a cached channel that lacks its overwrite list again, and denies the command when a fetched channel that is not a thread still lacks that listPermissions( names) - The
cooldownoption accepts{ durationwithMs, per } perset to"user","channel"or"guild", and uses a memory store owned by the router whenstoreis omitted - The router's
useoption adds middleware that wraps every command and can stop it by not callingnext. In both APIs,nextfails with the command's own error, and each middleware stage and the command run at most once per message, even whennextis called again or evaluated concurrently. A command failure is reported once with its command name, whatever the middleware does with it. Middleware that fails with a different error adds its own report after the command's failure - Setting
onsends a short explanation, such asReject: "reply" Missing name. Usage: !greet <name>, the guard's reason or the cooldown's remaining time. The commands ofrunuse it by default, while a router fromBot commandsgives no feedback unless.create onis set. SettingReject on, on a router or on one command, sends nothing, and the rejection is still counted and logged at Debug asReject: "silent" commands. An active cooldown key is answered once until its retry time, and a guard denial once per user and command every 5 seconds, while argument rejections are answered each time. A skipped reply still counts as a rejection and is logged at Debug with.rejected feedback. AnSuppressed onfunction receives every rejection. Native replies measure the remaining time with the handler's Effect ClockReject - Setting
mentionaccepts a mention of the bot,Prefix: true <@id>or<@!id>, as a prefix, and theprefixoption can be resolved asynchronously. The bot's ID comes from the user in the gateway's READY. When READY lacks it, one sharedusersread supplies it, and a failed read is logged at Warn and retried after a backoff that starts at 5 seconds and doubles up to 5 minutes.fetch Self - An unknown command name passed to
onand its DebugUnmatched commandslog record carry.unmatched suggestion, the closest registered command or group name when one is similar. Two swapped adjacent characters count as one edit, sopnigsuggestsping. Hidden commands and everything inside a hidden group are never suggested - The bound
replyaccepts a plain string - Argument descriptors add
min,maxanddefaultfor integers and numbers, and newduration,memberandcustomtypes. Amemberargument accepts only IDs in the 64-bit ID range, asiddoes. Acustomparse function is synchronous: A returned Err fails the command like a throw, and a returned Promise or Effect is not run as the value but fails the command withConfigurationfor fieldError command, without an unhandled rejection
Migration:
- The
runform is removed. Move event handlers intoBot( options, install, run Options) events, commands intocommands, other startup work intosetup(, andclient) runsettings such asOptions signalandprocessinto the options objectSignals - Code that checked
run's Result for configuration failures catches the thrownBot Configurationinstead, except for a missing or blankError token, which stays in the Result. The Result reports connection failures, cancellation and stopped workers as before, and a failedsetuporcommandscallback asApplication. A handler or command router set toError overflow: "stop"that overflows is reported and stops the bot, and the run fails withCritical, whoseWorker Stopped Error causeis the subscription'sEvent. SoOverflow Error Eventis no longer part of theOverflow Error runerror type in either API. MatchBot Criticalinstead in code that matches onWorker Stopped Error runfailures, or setBot overflowtodroporOldest dropto keep the bot runningNewest Criticalcounts event subscriptions in configuration order, followed by the command routerWorker Stopped Error .worker Index - A cooldown without
storeno longer requires one. Passperinstead of building the key from the message when the default key fits - Keyed
registerentries may omitMany argumentsfor unrestricted raw args. TypeScript projects withexactomit it rather than passOptional Property Types arguments: undefined - The commands of
runnow answer a rejected command. SetBot onin theReject: "silent" commandsoption ofrunto keep rejected commands unanswered. Unmatched commands, and rejected commands of a router fromBot commands, still get no reply unless.create onorUnmatched onasks for oneReject
- The
-
dd52acd: The
runfunction now skipsBot messageandCreate messageevents written by bots, including the bot's own messages, before they reach itsUpdate eventshandlers or itscommandsrouter, in both entry points. A reply can no longer trigger the handler that sent it, and handlers no longer need amessagecheck. The new.author .is Bot ignoreoption controls this in one place and defaults toBots true. Setting it tofalselets both the handlers and the prefix commands see bot-authored messages. Acommandssetting, when present, still decides for the prefix commands alone. A value other than a boolean is misuse. The.ignore Bots createfunctions of both testing entry points accept the same optionTest Bot Migration: A bridge, logging or moderation bot that handles messages from other bots passes
ignoretoBots: false run. Bots built withBot createandClient clientare not affected.on -
944c7bf: A missing or empty token no longer makes
runthrow in the default API or die in the Effect API. Once the other options are valid,Bot runreports it like any failed run before creating a client: It logs theBot Configurationonce without a stack trace, setsError processto 1 unless.exit Code reportisFailure false, and returns it as an Err in the default API or fails with it in the Effect API. Bothrunfailure types now includeBot Configuration. Other invalid options still throw in the default API and die in the Effect API, andError createstill rejects a missing token as misuseClient The missing-token hint now also covers projects created by
fluxerly init: SetFLUXER_in theBOT_ TOKEN .envfile that init wroteMigration: Code that caught the thrown
Configurationfor a missing token around a defaultError runcall reads it from the returned Err instead. Effect code that handled the defect handles the typedBot ConfigurationfailureError -
37612fd: A
runhandler set toBot overflow: "stop"whose queue fills now stops the bot in both entry points. The bot previously kept running without that handler, as its API comments said, so the handler's events stopped arriving while the bot looked healthy. The run now fails withCritical, whoseWorker Stopped Error detailshold theevent, the exceededlimit,messagesorbytes, and itscapacity, and whosecauseis the subscription'sEvent. The overflow itself is still reported toOverflow Error onor logged. A bot that should keep running setsError overflowtodroporOldest drop, which are unchangedNewest -
dd52acd: The default API's
runnow handles SIGINT and SIGTERM unlessBot processisSignals false. Ctrl+C or a stop from a process manager such as Docker, systemd or PM2 requests a normal stop that drains running work fordrain, then the listeners are removed and the process can exit. The runner still never exits the process itself, andMs processis no longer neededSignals: true The Effect API's
runkeeps process signals opt-in. A launcher such asBot Nodealready interrupts the program on these signals, and a second handler would race its interruption. SetRuntime .run Main processthere only when nothing else handles the signalsSignals: true Migration: An application that handles SIGINT or SIGTERM itself, or runs several bots in one process with its own shutdown, passes
processto the defaultSignals: false runBot -
7733b23: The
runfunction reports a failed run for the process by default, in both entry points. When the bot stops because of a failure, it logs that failure once as an Error record with codeBot lifecycleand sets.bot Failed processto 1. It skips the record when the client already logged the same error, such as the.exit Code lifecyclerecord of a rejected token, so the failure appears once. The failure is still returned, and a normal stop or an interruption reports nothing. The native API also reports a defect this way, including misuse found before any client exists, such as a missing token or a misspelled option key. In the default API, misuse throws.connection Ended Configurationand a defect rejects withError Sdk, which Node.js prints before it exits with a failing code. A bot entry point therefore needs no try/catch:Defect await runis complete in the default API, and so isBot( { ... }) await Effectin the native API.run Promise Exit( run Bot( { ... })) Migration: Code that already logs the returned failure and sets the exit code can drop that handling. To keep full control of the failure output and exit status, pass
reportFailure: false -
abb5d3f: Message search no longer fails when a result comes from an unnamed group DM, in both entry points. Fluxer now sends
name: nullfor such channels, soMessageis nowSearch Channel .name string | nullwhen present, and code that reads it must handle null -
7733b23: Forward each supervised child's output in the supervisor's console format by default, and add the
childsupervisor option withOutput prefix,inheritandignore. Readable output labels each line with its shard or child ID. JSON output stays valid JSON Lines: A child's SDK record gainsfields, another JSON object passes through unchanged and other text becomes a.child supervisorrecord. With.child Output prefix, children receive the supervisor's format and color throughFLUXERLY_andLOG_ FORMAT FLUXERLY_unless those variables are already set. Very long lines are truncated with a marker, forwarding respects output backpressure, and exit handling waits briefly for a child's final output, such as a crash stack trace. The supervisor now logs child start, exit, crash, restart and spawn failures, and spawn errors keep their original cause. The newLOG_ COLOR loggingsupervisor option accepts the client logging settings for these recordsMigration: Supervised child output was previously discarded. Set
childto keep discarding itOutput: "ignore" -
7733b23: Supervisors now restart a crashed child by default, with the documented defaults of
Supervisor: Up to 3 restarts in a row, delays from 1,000 ms doubling up to 30,000 ms, and a fresh budget after 60,000 ms of healthy running. Previously an omittedRestart Options restartoption meant that one crashed child stopped the whole supervisor and took every shard offline. Therestartoption now also acceptsfalse, and thesupervisorrecord names that setting when it stops the supervisor.crash Restart budgets now count consecutive crashes instead of every crash over a child's lifetime. A child that runs for the new
restart(default 60,000 ms) after finishing configuration gets its full budget and the first delay back at its next exit. When that restores used restarts, the.healthy Reset Ms supervisorrecord has.restart fieldsset to true. A crash loop still exhausts.budget Reset maxand stops the supervisorAttempts Migration:
- To keep the previous behavior, where one unexpected child exit stops the supervisor, set
restart: false - To keep a lifetime-wide budget in practice, set
restartto a period longer than the expected process lifetime, up to 2,147,483,647 ms. The.healthy Reset Ms restartscount instatus()child snapshots also starts again after such a reset
- To keep the previous behavior, where one unexpected child exit stops the supervisor, set
-
314ac29: Test clients in both testing entry points now answer message sends (
POST /) and edits (channels/:id/ messages PATCH /) that nochannels/:id/ messages/:id restregistration matches. The reply is a message by the bot account that echoes the request's content, embeds, flags and tts, so a first test needs no response fixture. A registered response still wins, and other unmatched requests still receive a 404 with a.respond testingWarn.unmatched Request Migration: A test that relied on the 404 for an unmatched message send or edit registers that failure with
rest.respond -
314ac29: Test waits in both testing entry points, such as
TestandRoute .next idle, no longer fail after a default 2,000 ms. Withouttimeoutthey wait until their condition holds, the test client shuts down or, in the Effect API, the wait is interrupted, so the test runner's own timeout ends a test that hangs. An explicitMs timeoutstill fails the wait withMs TestTimeout Error Migration: A test that expects
Testfrom a wait without options passesTimeout Error timeoutMs -
dd54820: Threads, forum channels and media channels now have their own channel shapes in both entry points, instead of arriving as
Guild.Unknown Channel ChannelgainsType Announcement(10),Thread Public(11),Thread Private(12),Thread Forum(15) andMedia(16), andGuildgainsChannel Guild,Announcement Thread Channel Guild,Public Thread Channel Guild,Private Thread Channel GuildandForum Channel Guild. A thread flattens Fluxer's thread metadata, such asMedia Channel archived,lockedandauto, and carries the bot's ownArchive Minutes membershipwhen Fluxer reports it. Theisfunction selects every thread type. A forum or media channel carries its tags and post settings, each optional because Fluxer leaves them out of some payloads, such asThread Channel guild. Text and announcement channels gainChannel Delete defaultandAuto Archive Minutes default. The new constants areThread Rate Limit Per User Channel,Flags Thread,Auto Archive Minutes ForumandSort Order Forum. TheLayout channelsand.get channelsmethods can return any of these shapes, and.fetch channelsnever lists threads.fetch All PermissionsgainsManage,Threads Create,Public Threads CreateandPrivate Threads Send, soMessages In Threads permissionandBits guardsaccept their names..require Permissions MessagegainsType Thread(18) andCreated Thread(21).Starter Message Messagegains the server-managed bitsFlags Has(32) andThread Failed(256), which message and webhook send and edit operations reject before dispatch.To Mention Some Roles In Thread Messageholds the thread a message started, and.thread Guildreports whether the bot can use threads in a community. The testing fixtures gainList Summary .threads Active thread()andforumwith theChannel() WireandThread Wiretypes, andForum Channel guildnow includes an emptyCreate() threadslistThe new
clientnamespace creates, changes, lists and searches threads and manages their members. Its.threads createmethod starts a thread in a text or announcement channel,createstarts one on an existing message, andFrom Message createcreates a forum or media post with its first message, sending its files in the same multipart request. ThePost editmethod archives, locks, renames or pins a thread and replaces a post's tags. For reading,fetchlists a community's active threads,Active fetchreads one page of a channel's archived threads, andArchived searchreads one page of matching threads, with the first message of each post in a forum or media channel. Every list carries the bot's ownmembershipon each thread it joined. A search that Fluxer answers with HTTP 202 while it builds the community's search index returns{ indexing: true }without a retryThread membership changes through
join,leave,addandMember remove, and thread members are read withMember fetch,Member fetchandMembers iterate. WithMembers with, each member also carries the user's community membership, decoded with the thread's community from the channel cache or from one extra channel read. Failures areMember Channelvalues whose operation names theOperation Error threadsmethod, and a missing-permission rejection ofcreate,create,From Message create,Post edit,fetch,Archived search,addorMember removelists the permissions that method needsMember Forum and media channels can be created and managed through
channels. Creating a channel acceptsChannelandType .Forum ChannelwithType .Media available,Tags default,Reaction Emoji default,Sort Order default,Tag Setting flags, atopicof up to 4,096 characters and, on forum channels,default. Text and announcement channels acceptForum Layout defaultandAuto Archive Minutes defaulton creation, andThread Rate Limit Per User channelsaccepts those two settings and every forum setting, including a complete.edit availablelist. On creation, a setting that the chosen channel type cannot have fails with reasonTags inputbefore any request. An edit cannot tell the channel's type, sochannelsaccepts every setting and leaves that check to Fluxer. Both methods fail with reason.edit inputbefore any request when a value has the wrong format or range, such as more than 20 tags, while checks that need the channel or community, such as whether a tag emoji exists, stay with Fluxer. The newchannels,.create Forum Tag channelsand.edit Forum Tag channelschange one tag and return the updated forum or media channel. The.delete Forum Tag editmethod replaces the tag, so an omittedForum Tag moderatedvalue or emoji resets. A permission rejection of any of the three listsViewandChannel ManageinChannels details.required Permissions Webhook clients can now reach threads.
WebhookgainsMessage Input thread, sent as theId thread_query parameter, and, on new messages,id threadto start a forum or media post with up to fiveName applied. The SDK rejectsTag Ids threadtogether withName thread,Id appliedwithoutTag Ids thread, andName threadon a reply or forward before any request. TheName fetch,Message editandMessage deletemethods takeMessage Webhookwith aMessage Operation Options threadto reach a message in a threadId Thread gateway events now have their own typed events. The
threadevent delivers aCreate Thread, the thread plusCreate Event is, which is false when the bot joined or was added to an existing thread. TheNewly Created threadevent delivers the changed thread,Update threadaDelete Threadwith the thread's ID, community, parent and type,Deletion threadaList Sync Threadwith the bot's active threads in a community or in some of its channels, andList Sync threadaMembers Update Threadwith the members who joined and the IDs of those who left. TheMembers Update "channel"handler partition keys thread events by their thread. Automatic event filtering suppresses unused thread dispatches, keeps every thread dispatch while the channel cache is enabled and keepsTHREAD_while the message cache is enabledDELETE The channel cache now holds threads. A community snapshot fills it with the active threads the bot can view, leaving out a malformed thread with a
gatewayWarn record instead of failing the snapshot. Thread create and update events store the thread, a thread deletion removes it, and a thread list replaces the cached threads it covers. The.thread Skipped threadslist also enters the cache, while archived pages, search results, forum posts and member reads do not. A change to the bot's own membership of a thread removes that thread, and a completed Resume removes the resumed shard's threads, because Fluxer never replays thread lists. A.fetch Active channelsresult keeps cached threads, and an operation confined to one thread clears only that thread.fetch All Deleting a text, announcement, forum or media channel deletes its threads without thread deletion events. The channel cache removes the threads whose parent is the deleted channel, and the message cache removes the messages of every channel in that community, or without community context, that the channel cache does not hold. Without the channel cache that is every such message, because a message does not record its thread's parent. A thread deletion removes the thread's messages
Permission checks now handle threads. A thread has no permission overwrites of its own, because Fluxer calculates its permissions in its parent channel and then grants
Sendin the thread exactly whenMessages Sendis granted. The newMessages In Threads Permissionsupplies that parent toInput .parent Channel permissions, which applies the same rule. A thread without its parent, a thread with a different channel as.calculate parent, and aChannel parentbeside a channel that is not a thread fail with reasonChannel input. For a thread,permissionsalso reads the parent channel under the same deadline. The.fetch guardsguard now reads the thread and then its parent channel, each from an enabled channel cache before a request, and decides by the same rule, where it previously used only the member's role permissions and ignored the parent channel's overwrites. None of these checks covers whether the member can see a private thread or whether the thread is archived or locked. The guard also denies a command, with the reason "This command cannot confirm this channel's permissions", when a fetched channel that is not a thread lacks its permission overwrite list.require Permissions Message search no longer fails when a result comes from a thread. Fluxer lists the channel of such a result in a separate thread list, which previously made
messagesreject the whole page and stopped.search messages..iterate Search Messagenow holds those threads as typed thread shapes, each with the bot'sSearch Results Page .threads membershipwhen the bot has joined it, andchannelskeeps the other channels. Each message's channel appears exactly once acrosschannelsandthreads. Audit-log pages andguildevents also no longer fail on a forum or media channel change, because itsAudit Log Entry Create available_andtags default_values now decode asreaction_ emoji Forumlists andTag Forumobjects, whichDefault Reaction Auditgains, andLog Change Value Auditlists the threads that the page's thread actions targetLog Page .threads Rejections of thread, forum tag and webhook post operations carry a reviewed
api. The newError Apicategories areError Code thread,Archived thread,Locked thread,Already Created channel,Has Threads invalid,Channel Type forum,Tag Names Not Unique forum,Tag Required no,Tags Available media,Channel Required webhookandForum Target Required webhook, each with a hint that names the fix. The active thread, forum tag, pinned post and thread member limits joinThread Name Requires Forum resource, and a missing forum tag or thread member joinsLimit unknown.Resource Apigains the matching Fluxer codesProvider Code Migration: Code that matched
type === "unknown"for a thread, forum channel or media channel now receives the typed shape, and an exhaustiveswitchoverGuild,Channel Channel,Type ChannelorCreate Apineeds the new cases. AError Code permissionscall for a thread passes the parent channel as.calculate parent. AChannel gatewaylist that names a thread dispatch an enabled cache needs, which is any thread dispatch for the channel cache and.ignored Events THREAD_for the message cache, now fails client creation withDELETE Configuration, so remove the name or turn off that cache. With only the message cache enabled, deleting a text, announcement, forum or media channel now evicts every cached message of its community and every cached message without community contextError -
7733b23: Check every validated timestamp, whether received from Fluxer or supplied as a pin cursor, community history cutoff or embed time, with one ISO 8601 grammar: A date,
T, hours, minutes and seconds, an optional fraction of any length and aZor±hh:mmoffset, on a real calendar day. Accepted values stay exactly as received or supplied. Custom status expiry inputs keep their own rules, which also allow omitting seconds, and attachment expiry times remain unchecked text.Guildnow accepts an offset instead of only UTC, discovery times and community history cutoffs from Fluxer accept offsets, and pin cursors and message, pin and community times accept fractions longer than nine digitsEdit .message History Cutoff Migration: Member join and timeout times, invite times, ban times and channel last-pin times received in any other form, such as without seconds or a zone, are now rejected. Before, only the date was checked and
Datehad to accept the value.parse -
7733b23: Every operation that takes an input, query or options object rejects a key it does not support before sending anything, in both API styles. The failure is the operation's own error with reason
inputand aninputconstraint ofValidation allowed. Its explanation names the key, suggests the closest supported key and lists the supported keys, for exampleFields Unsupported option "timeout" in the operation options. Did you mean "timeoutMs"? Supported options are timeout Ms, signal Operations that sent the request and ignored an unknown key before now reject it: The options of message sends, replies, forwards, edits, deletions, fetches, searches, pins and reactions, the OAuth
authorization,Url exchangeandCode revokeinputs, and the default APIgatewayoptions. Channel position entries and permission overwrites report an unsupported key separately instead of as a general format failure. The.send instanceand.resolve cacheoptions name an unsupported key and suggest the closest one in their.entries ConfigurationError Migration: Remove keys that an operation does not support from its input, query and options objects. Each failure names the first unsupported key it finds
-
7733b23: Log startup, readiness, lost connections with the close-code meaning and the real next step, session resets, long rate-limit waits, event drops, shutdown and every application failure by default. Records share one shape with a level, category, stable code, message and safe fields, and never contain the token, authorization values, client secrets, webhook tokens or invite codes
Configure output through
loggingwithlevel, per-categorycategories,debug,format,sink,dedupeandunsafesettings, or setFLUXERLY_or a list of categories for Debug records. The default API prints readable lines in a terminal and JSON lines otherwise, deciding separately for standard output and standard error. Readable lines show the record code after the category, print safe details for every error in the cause chain and collapse stack frames inside the SDK and Effect into one line. TheDEBUG=1 FLUXERLY_andLOG_ FORMAT FLUXERLY_environment variables choose the format and color for a client withoutLOG_ COLOR format. The color variable accepts1,true,yesoronto force color and0,false,nooroffto disable it, and an unrecognized value of either variable logs onesdkWarn. Without.unknown Environment Value sinkorformat, the native Effect API logs through the caller's Effect logger withfluxerly.*annotations, includingfluxerly,.error .code fluxerlyand.error .hint fluxerlydetails for a failure. Each error in the cause chain adds its own annotations under.error.* fluxerly, then.error .cause. fluxerlyand so on. Its records carry masked copies of failures that show their code, so Effect's cause printer never shows credentials or extra error properties. Repeated Warn and Error records are collapsed with a repeat count, and.error .cause .cause. diagnostics()counts failures, drops, retries, rate-limit waits, reconnects and resumes. A rate limit whose wait would pass the request deadline fails the request at once, logs.counters ratelimitand is not counted as a wait. Failure reports logged instead of reaching an.deadline onhook carryError fields(.report Outcome queue,Full interrupted,clientorClosed subscription), and collapsed records carry the suppressed count inClosed fields. The.repeated lifecycleand.retry lifecyclerecords carry.connection Lost fields, which is.next resumeoridentifyand names the next handshakeUnsafe payload logging prints its Warn banner once before the first payload record at any level, prints nothing for a
silentcategory and reads at most the first 64 KiB of each received REST bodyThe native API also records
fluxerly.*tracing spans for gateway connection, REST requests, event handling and command execution, plus REST duration, rate-limit wait, reconnect, drop and handler-failure metricsMigration: The
development,measurements,minimumandLevel loggerlogging settings,fromandStructured Logger fromare removed. UseEffect Logger levelandcategoriesfor levels,debugorFLUXERLY_for Debug records, andDEBUG sinkto forward records to another logger. The typesDefault,Logger Default,Logging Options Structured,Logger Sdk,Lifecycle Event Sdk,Lifecycle Log Record Sdk,Operational Log Record Sdk,Measurement Log Record SdkandMeasurement Operation Sdkare removed.Measurement Stage Sdkis renamed toLog Level Logand its values are now lowercase, such asLevel "info". TheAllandNonevalues are removed, andlevel: "silent"replacesNone.Sdkis renamed toLog Record Log, which has a new shape. Clients now print startup, readiness, connection loss and shutdown at Info by default. SetRecord level: "warn"to print only problems. The Effect API'sMessageis nowCache Options<E, R, M> Message, because cache callback failures go to the client'sCache Options<M> onrather than a cache-specific hookError -
abb5d3f: Add
Webhookand theType Incoming,Webhook ChannelandFollower Webhook Unknownshapes to both APIs. Received metadata now requires a kind and preserves future numeric kinds asWebhook type: "unknown"withrawType Bot clients can rename, move or delete follower webhooks but cannot change their avatars. Deleting a follower webhook stops its channel following the source. Optional frozen
sourceandGuild sourcesnapshots reflect source visibility to the original creator, and their absence does not prove deletionChannel Webhook metadata no longer carries tokens. The
Createdfield and webhook-clientWebhook .webhook fetchandeditresults now useIncoming, and creation requires a valid incoming token before returning credentialsWebhook
Minor Changes
-
2c9dfde: Behavior change: A channel write no longer clears the whole channel cache. A write to a cached channel removes only that channel, plus its cached threads when the write deletes the channel or converts its type, and its cached forum posts that apply a tag the write removes, as
channelsand a.delete Forum Tag channelswith.edit availablecan, because Fluxer drops removed tags from posts without a thread update. Removing tags also stops pending reads from storing that forum's posts, while other pending reads by ID and reads of other communities still store what they return. A write to a category, and a write that names a community, such asTags channels,.create channelsor.reorder guilds, removes only that community's cached channels. A write to a channel the cache does not hold still clears every cached channel. Code that relied on the full clear can call.leave clientafter the write.cache .clear( "channels") Behavior change: A
guildevent for a forum or media channel now removes the channel's cached posts that apply a tag missing from the update'sChannel Update available, because Fluxer removes a tag through that update alone and drops it from posts on read. Fluxer gives each new tag a new ID, so posts whose tags all remain stay cached. Every such update also stops pending reads from storing that forum's posts, because a read of the forum can store its new tags before the update arrives. Other pending reads by ID and reads of other communities still store what they return, so an update in one community does not keep an unrelated read from filling the cache, while a pending read of the updated channel's own community stores nothing, as after any channel updateTags Behavior change: A
guildevent for a text or announcement channel now removes that channel's cached threads whose type the channel's type cannot hold, because Fluxer retypes a converted channel's threads and sends no thread update for archived ones. This works whether the channel was cached before the update or not, and after a read already stored its new type. Every such update also stops pending reads from storing that channel's threads, while other pending reads by ID and reads of other communities still store what they returnChannel Update Behavior change: A shard's first session now releases its communities' cached snapshots, as every later new session already did, and stops pending reads from storing them. Community, member, role, emoji, sticker and channel snapshots read through REST before connecting no longer outlast the first READY, because the events between that read and the session were never received. The release happens as READY arrives, before the communities Fluxer then sends again through
guild, which refill the community, role, emoji, sticker and channel caches. This also fixes later new sessions, which until now could release those communities after they arrived, leaving the caches empty until each community was read again. Read again after connecting to keep other snapshots, such as members. A saved session resumed throughCreate shardingis not a new session and keeps them.sessions The new
rolesmethod returns every cached role of a community, including everyone and ordered like.get All( guild Id) roles, or.fetch All undefinedunless the cache knows the list is complete. Aguildevent makes it complete, as does an uncontestedCreate rolesread that finishes while the community's shard is connected, because only role events keep the list current. A full read that finishes while the community's shard is disconnected therefore does not make the list complete, while a list that was already complete stays complete through a recovery that resumes the session. Role create and update events keep it complete. Removing any of its roles, a new session for its shard and every successful role write by the bot, including create, edit and reorder, or one whose outcome is unknown, make it unknown until the next.fetch All guildor full read. TheCreate rolescache limit counts the roles of every community together, so a bot in many communities needs a largermaxto keep complete listsEntries The new
membersmethod returns the bot's own cached member of a community, or.get Self( guild Id) undefinedwhen it is not cached or the bot's account ID is not yet known from READY or amembersresult. With.fetch Self cacheenabled, each.members guildnow stores the bot's own member. TheCreate guildevents sent while a session starts list member accounts by ID only, so the SDK completes the bot's own member from READY's account and leaves out the others. ACreate guildsent after startup, such as for a community the bot joins or one that becomes available again, lists complete members, and the SDK stores all of them, which are the bot's own and those in voice channelsCreate The new
Shardfield identifies a shard's current gateway session. It is 0 until the shard first starts a new session in this client, so a process that resumes a saved session after a restart reports 0 until that shard next starts a new session. It changes, without repeating, whenever the shard starts a new session, including after a move to a larger plan. A Resume keeps it. A changed value means that shard's community events may have been missed, and Fluxer then sends each community again throughState .session Epoch guild. It is the shard's current value, so an event still waiting in a handler's queue can already see a newer oneCreate The new
Eventfield gives the receiving shard'sContext .session Epoch sessionwhen the client accepted the event, so an event received before a new session keeps its earlier value even when its handler runs after the new session started. CompareEpoch contextper community, not per shard, because one shard serves many communities. Events delivered through the testing entry points carry the epoch of the test gateway's session in the same way.session Epoch The new
contextfield ofBotgives eachEvent Context runevent handler, in both APIs and in bots run byBot create, the sameTest Bot Eventthat aContext clienthandler receives, so a.on runbot can compareBot contextper community as well.session Epoch -
321d8bb: The
commandsnamespace has two new helpers in both APIs, and the quoted parser and its rejections changed. The newcommandsfunction returns the candidate closest to a misspelled name, using the rule behind the.suggest( name, candidates) suggestionofCommand: Case is ignored, one edit is allowed for names of up to four characters and two for longer ones, an adjacent swap counts as one edit, and the earliest of equally close candidates wins. It returnsUnknown Name undefinedwhen none is close and for an empty name. It agrees with the router for its default case-insensitive matching when given the registered names of one level, while the router also considers aliases and reports the command's own name for a near alias. The newcommandsfunction converts one word with any built-in argument descriptor except.parse Argument( token, descriptor, options?) custom, using the router's own grammar. It returns the descriptor's value type,undefinedfor a rejected word, and the descriptor'sdefaultorundefinedfor a missing one. The newCommandtype carriesArgument Parse Options guild, whichId memberdescriptors need. Invalid input to either helper, such as a bad descriptor, a non-string name or candidates that are not an iterable of strings, throwsConfigurationin both APIs, like other registration misuseError Prefixand theCommand Parse Rejection Commandoutcome ofParser Rejected onhave an optionalUnmatched name, the command name the parser read before it declined, as typed. Thecommandsparser sets it for an unclosed quote and a trailing backslash, a custom parser may set it, and the router passes it through, so a reply can say which command a syntax mistake was for. A name that is not command-name syntax is reported as a parser failure. Existing parsers and callbacks keep working, because the member is optional..parse Quoted -
0f8a442: Add
defineto the default and Effect entry points. It returns a bot'sBot runoptions without the token unchanged, and keeps the types of command arguments and, in the Effect API, of handler errors and services. A module can then export the options for bothBot runandBot createTest Bot The starter bot in
examples/now exports its options asstarter botwithdefine, answersBot !pingthrough thecommandsoption instead of matching the message text, and starts only when Node.js runs the file. It now also answers!pingfollowed by more text or in other letter cases. Thefluxerly initcommand writes these shipped files instead of its own copies, so the generated bot is the one the documentation shows, and its generated test importsbot. The repository examples, the quick start, the testing guide and the README use the same shape -
5717638: Behavior change: The default
maxof an iteration now grows withPages max, so a caller who sets onlyItems maxgets up to that many items. It is the pagesItems maxneeds at the page size plus 100 spare pages for short pages and skipped duplicates, instead of a fixed 100. For example,Items messageswith.iterate History maxand the default page size of 50 used to fail withItems: 10000 PaginationreasonError pageafter 5,000 items and now reads all 10,000. This applies toLimit iterate,History members,.iterate members,.iterate Search guilds,.iterate audit,Logs .iterate iterate,Reaction Users iterate,Pins threadsand.iterate Members messages, whose pages stay capped at Fluxer's 400-page ceiling. The default stays finite, so a server that keeps returning short pages still ends with.iterate Search page. An explicitLimit maxis used as given, including a value above or below the new default, except thatPages messagesnever reads more than 400 pages. Code that relied on the old default to stop a long scan early can set.iterate Search maxPages: 100 Behavior change: The
directandMessages .open directmethods no longer clear every cached private channel when they start. A successful opening replaces only the cached snapshot of the channel Fluxer returns, because opening a conversation changes no other channel. The overlap rules change in detail. A read that started before the opening is still not stored. A read of another private channel that overlaps the opening can now stay cached, because the opening no longer clears the cache when it finishes. A conflict involving the returned channel evicts that channel, and a stale successful opening evicts only its own channel. A failed opening that was started still clears the direct-message cache, because its outcome may be unknown and no channel is known to evict. Invalid input, such asMessages .send open(, fails before the cache is touched and leaves it intact. Code that relied on the full clear can call"bad") clientafter the write.cache .clear( "direct Messages") The
Paginationhint for reasonError pagenow tells callers to setLimit maxexplicitly or lowerPages max, and to narrow the filters of a message search, which reads at most 400 pages. It no longer suggests a largerItems page, because the defaultSize maxshrinks asPages pagegrowsSize -
321d8bb: Three additions to the shared
errorsobject and thetexthelper, plus two fixes.The new
errorsfunction returns whether a failed request may have reached Fluxer, as.outcome( error) "not,Dispatched" "rejected"or"unknown". It reads theoutcomeof any SDK error that has one and, for an EffectCause, the least certain outcome of its failures. It returnsundefinedwhen the SDK cannot establish an outcome, such as forCancelled,Error Sdk, non-SDK values and aDefect Causewith a defect, an interruption or a failure without an outcome, so the caller applies its own policy. Only"notshows that a write was not sent, so treat every other value, includingDispatched" undefined, as a write that may have applied and reconcile before sending it again. ACausecovers only the failures it kept, and Effect's concurrentforandEach alldrop the reasons of siblings they interrupt, so for several concurrent writes check each item's own result, for example with{ mode: "result" }. Wrapping an SDK call inEffectyields only a.timeout Timeout, so use the operation'sError timeoutoption to keep the outcome.Ms The new
errorsfunction is true for an SDK error with HTTP status 404, including a 404 that.is Not Found( error, resource?) messagesreports with reason.send rejected. Pass aNotsuch asFound Resource "member"or"channel"to also require Fluxer's matchingUNKNOWN_*code. A 404 says only that the resource is missing, not why: Fluxer answersUNKNOWN_for a missing channel and also for message and reaction requests in a thread from a bot that is no longer in the community, while other requests from such a bot get a 403CHANNEL missing. A 404 without a recognized code is true without a resource and false with one. Any other resource name throwsPermissions Helperwith operationError errorsand the new reason.is Not Found resource. TheNottype is exported from both entry points.Found Resource The new
textfunction shortens text to at most.truncate( content, { max Length, suffix? }) maxUTF-16 units and ends it withLength suffix(default"…") when anything was cut. It never splits a surrogate pair and does not trim or look for word boundaries. Invalid text or options throwHelperwith operationError text. The.truncate Texttype is exported from both entry points.Truncate Options ClientgainsClosed Error outcome, an addition. A REST call ended by a closing client now reports how far it got:notwhen the client was already closed or the request was still queued,Dispatched rejectedwhen closure came while waiting out a 429 before a retry, andunknownwhen the request was in flight. The outcome is also recorded indetails, and an.outcome unknownoutcome adds a hint to check whether Fluxer applied the change before repeating it with a new client. Closures that are not a REST request, such as gateway commands, collectors, waits, counts, member chunks, downloads and connecting, keepoutcomeasundefined. The preparation steps of a message send, namely opening a direct message and uploading attachments, count as not sent for the message, so a closure during them reportsnotfor the message request even if the earlier step took effect. A closure that cuts off a read recordsDispatched detailsand gets no hint to check for an applied change. The POST reads.read messages,.search membersand.search attachmentscount as reads in a closure and in their other failures, so their network and timeout failures are now retryable by.refresh Urls errors. An OAuth request that closure cuts off in flight still fails with.is Retryable OAuthreasonError networkand outcomeunknown, and a closure during OAuth instance discovery now reportsnot. A message cleanup closed while a batch waits out a rate limit now reports that batch'sDispatched rejectedoutcome instead ofunknown, and a closure between batches already reportednot. The constructor takes an optionalDispatched { outcome, read }object, so existingnew Clientcalls are unchanged.Closed Error() Partitioned
invitehandlers now order events per community. They previously ignored the invite's nestedCreate guildand.id channel, so every invite shared one key and ran one at a time. With.id partition: "guild", invites of different communities run side by side while one community's invites stay ordered, and a group invite without a community is keyed by its channel.The embed input documentation now states Fluxer's limit of 6,000 characters of combined embed text per message. The SDK does not check it locally, and exceeding it fails remotely with reason
rejectedand aninvalidAPI error.Form Body -
78579fe: Add
messagesto both APIs. It answers whether one user currently has a given reaction on a message, with one request and no cache, so bots with reaction roles, verification or voting can recheck a reaction after being offline. A deleted or unavailable message fails instead of answering false.fetch Has Reaction -
b1b9d72: Fields that Fluxer documents on gateway events and the SDK used to drop now reach handlers, in both APIs
A
messageevent carriesCreate mentioned,Here channelandType nicknames, and themessageoption accepts them. They are set only on a created message, soFields messageevents, REST reads and the message cache never carry them, and a cached message never mixes them with a later update. Fluxer sends the here mention only asUpdate true. The channel type is Fluxer's number, kept as received, with 1 for a direct message and 3 for a group direct message. The nicknames of a group direct message are a frozen object keyed by user ID, likeDirect. A value of the wrong type rejects the created message, likeMessage Channel .nicknames mentionedEveryone A
messageevent carriesDelete member, a new frozenDeletedthat is aMessage Member GuildwithoutMember usernameandis, because Fluxer removes the member's user. It is left off when Fluxer sends none, when the deletion names no community or author, and when it cannot be read, which never costs the event and logs nothing because no cache is involved. The member cache does not store itBot Voice states from
voice,State Update voiceand the call events carryState Snapshot isandE2ee Capable is, which readPeer To Peer falsewhen omitted, andversion, which is absent when Fluxer sends none. Behavior change: A voice state whosee2ee_orcapable p2pis not a boolean, or whoseversionis not a non-negative integer, is now rejected like the other malformed voice state fieldsA
guildevent carriesCreate member,Count onlineandCount joinedas Fluxer reported them when it delivered the event. They are optional because the SDK keeps accepting a body without them, and a value of the wrong type rejects the event. The guild cache does not store them, soAt guildsremains the way to read current totals.fetch Counts The testing entry points'
Wireaccepts optionalMessage channel_,type nicksandmention_, sohere fixturesemits a created message in full.message( { channel_ type: 3 }) -
8b70a5c:
GuildletsFeature Toggles .Visits Enabled guildskeep community visits on. Fluxer now enables.edit VISITS_for new communities and removes every toggleable feature that aENABLED featurelist leaves out, so an edit without it turns visits off and ends current visits. Before this, the SDK rejectedToggles VISITS_locally, so everyENABLED featureedit turned visits offToggles -
f0f217a:
Resolvedhas a newInstance discoveryflag, in both APIs, that reports whether the instance has community directory discovery switched on. Fluxer documents a missing flag as enabled, so it readsEnabled truewhen the instance omits it. While it isfalse, the discovery namespace'ssearch,apply,editandwithdrawfail with reasonrejected, HTTP400anddetails.provider Code DISCOVERY_, whileDISABLED fetchandStatus fetchstill answer, andCategories fetchthen reportsStatus eligibleasfalse. The value is read once when the client resolves the instance. Behavior change: A discovery document whosefeaturesis present but not a boolean is now rejected as a protocol failure, like a malformed.discovery_ enabled presigned_attachment_ uploads -
7611ba6: Behavior change:
messagesnow returns the task's own result when a later typing refresh fails. Before, the default API returned.keep Typing Errwith the typing failure even when the task succeeded, which lost the task's value, and the Effect API combined the refresh failure with the task'sCause. Now a refresh failure stops the refreshes without interrupting the task, and it logs one Warn record with the new coderest, whose.typing Refresh Stopped fieldsnames the channel and whose.channel Id errorholds the typing failure. A task that succeeds returns its value, and a task that fails returns only its own failure. A refresh stopped by client closure is not logged. The first typing request still has to succeed before the task starts, and its failure still fails the call. A defect in the refresh work still surfaces asSdkin the default API and in theDefect Causein the Effect APICode that relied on the
Error the combinedCauseto learn about a failed refresh should read therestrecord from the logging sink instead..typing Refresh Stopped Before, client closure during the task also returned
Client(Closed Error Errin the default API,Failin the Effect API), and now the task's own result is returned. Code that detected shutdown that way should watch the client'sstateorobserveinsteadState -
c892c2c: Both
Clientinterfaces have a new read-onlylastproperty: The receipt of the most recently accepted event, asReceipt Eventnumbers events, or 0 before the first. It is read live and counts every accepted event, whether or not a handler receives it. Work that is not an event, such as a timer or a command's follow-up, can record it when it starts and treat any event with a larger receipt as newer, for example to drop a result that a later edit made stale. Under a supervisor each child process numbers its own events, so values from different processes cannot be comparedContext .receipt -
470983c: The member that Fluxer attaches to community events now reaches handlers. The
memberfield ofmessageandCreate messagemessages, ofUpdate messageandReaction Add messagereactions, and ofReaction Remove typingnotices carries the author's or actor'sStart Guild, read from the community's current state when Fluxer sends the event. A message's member is completed with the author's username and bot flag. The field is absent on REST reads, direct messages, webhook messages, users who left the community,Member messageevents (Fluxer sends no username there) and batch or clear events. TheDelete messageoption now acceptsFields member, and the message cache keeps messages without it, so read it in the handlerA member that Fluxer sends in a form the SDK cannot read, including a reaction or typing member without the user's name, is left off the event and the event still arrives. This differs from voice states, which reject the whole voice state
Behavior change: With
cacheenabled, these events also store their member in the member cache, whichever.members messagethe client selects, soFields membersanswers for an author without a REST read. A member that cannot be read removes the account's cached member and logs.get gateway, because the cached copy may now be stale. An event rejected as malformed that carried a member removes the account's cached member too, or the community's cached members when the event does not identify the account, or every cached member when it does not identify the community either. The exception is that a reaction or typing member without the user's name is completed from the member already cached. The cache still requests no extra gateway dispatches, and a voice state's member is still never stored because it can lag behind role edits.member Skipped The testing entry points'
Wireaccepts an optionalMessage memberwithout its user and guild ID, sofixturesemits a decorated message in tests.message( { member }) -
b54fbd4:
clienthandlers now run up to eight messages at a time by default in both the default and Effect APIs, matching.on( "message Create", ...) runand the command router. Before, aBot createbot ran them one at a time, so one slow message handler stalled every later message, while aClient runbot did not. Handlers of other events still run one at a time unlessBot partitionis set, which also allows eight. Messages can now finish out of order, so a handler that must see them in order passes{ concurrency: 1 }for strict order or{ partition: "channel" }to keep each channel in order -
18d4a84: Both entry points gain helpers for Fluxer's permission overwrite rules, one call for a member's permissions in every channel, error helpers for retry waits, and an arrival number on every event
The new
permissionsmethod returns one member's permission bits in every channel of a community. Pass the bot's own ID from.fetch Channels( target, options?) clientas.users .get Self() userto see what the bot can do in each channel. It always reads fresh: The guild, the member, the roles and the channel list, once each and under one deadline. It then calculates each channel locally, the same way asId permissionsand.calculate permissions. The result is a frozen array of frozen.fetch Channelentries (Permissions { channel, permissions }), in the order Fluxer lists the channelsFluxer lists only the channels the bot can view, and no threads, so call
permissionswith a thread's ID for one. The.fetch optionsare the plainRequest, withoutOptions prefer, because no cache holds a complete channel list. Failures useCache Guildwith the new operationOperation Error permissions, and errors from the reads keep their own type. A listed channel that arrives without its permission overwrites fails as it does in.fetch Channels permissions, with reason.fetch response, outcome"unknown",detailsand.read details.channel Id The new
permissionhelper returns, as a bigint, the bits Fluxer would refuse in an overwrite change. Unless the bot has Administrator, it can allow, or stop denying, only permissions it has in the channel, and Fluxer answersBits .refused Grants( current, next, held) missingfor the rest. Pass the overwrite's currentPermissions allowanddenyascurrentand the new ones asnext, withundefinedfor no overwrite, so a removal passesundefinedasnext. Pass the bot's permission bits in that channel asheld. The helper returns0nwhenheldincludes Administrator, and invalid input throwsHelperwith operationError permissionBits .refused Grants The documentation of
channels,.create edit,reorder,setandPermission Overwrite removenow states this rule for each method. Their missing-permission hints name the channel that Fluxer checks and point to the helperPermission Overwrite The new
permissionhelper compares two overwrite lists as Fluxer does: The same target IDs and, for each, the same type, allow and deny, in any order. Fluxer copies a category's overwrite change to every child whose overwrites equal the category's, with no way to opt out, so compare the children with the category before changing it. A list that is not valid, or that names a target ID twice, throwsBits .same Overwrites( first, second) Helperwith operationError permission, because Fluxer rejects duplicate IDs on writeBits .same Overwrites The new
errorsfunction returns how many milliseconds an SDK error says to wait before a retry, read from the.retry After Ms( error) retryof any operation error orAfter Ms Rate. For aLimit Error Shardit returns the wait of the failure that stopped its shard, and for an EffectConnection Error Causethe longest wait among its failures. It returnsundefinedfor an error without a usable wait and for any other value, and never throwsThe
errorsfunction now also accepts an Effect.is Retryable( error) Cause, aserrorsdoes. It is true only when the.outcome Causehas at least one reason and every reason is a retryable failure, so a defect, an interruption or an emptyCausegivesfalseEventhas a newContext receiptfield that tells which event arrived first. The client numbers each event when it accepts it, starting at 1 and counting across all of its shards in the order events arrive. Every handler of an event sees the same number, and events decoded from one frame, such asguildand itsCreate voice, get consecutive numbers in dispatch order. Handlers of one community can run out of order because of concurrency, partitions orState Snapshot partition, so compare receipts to tell which event came first. Under a supervisor each child process numbers its own eventsReady A validation error with Fluxer's code
INVALID_now has an SDK explanation in its details. Fluxer sends this code for a URL field it does not accept, such as text that is not an http or https URL, a URL with a user name or password, or a host without a top-level domainURL_ FORMAT Auditnow documents the shape Fluxer records: A change with the keyLog Actions .Member Role Update roleswhoseoldandValue neware the member's full role ID lists, so the added roles are those inValue newthatValue oldlacks. AValue Memberentry that changes roles carries the same changeUpdate Testhas a newDisconnect Options endoption, defaultSession false, in both testing entry points. Withend, the test gateway forgets the shard's session beforeSession: true disconnectcloses the connection, so the client's Resume gets Invalid Session and the client identifies a new session. As after a real session loss, the shard'ssessionincreases, the next event's context shows the higher value, and the cache release and new-session handling run. Use it with a recoverable close code, because a fatal code still ends the client. A value that is not a boolean throwsEpoch Configuration, and the nativeError disconnectdies with itWithout
end, a disconnect resumes the same session after every recoverable close code, including 4007 (invalid sequence), because the SDK starts a new session after that code only when it closes a Resume attempt.Session Testnow says thisDisconnect Options .code -
80e4a00: Partitioned event handlers can now wait for a partition's own setup without holding a handler slot, and a full partitioned queue no longer lets a longer backlog take room from a shorter one. The
partitionoption ofReady clientand of a.on runevent handler (Bot Event,Handler Options Bot) takes the partition key, and in the default API a signal that aborts when the subscription stops, and returns a promise. In the Effect API it takes the key and returns an Effect that runs with the services available at registration. Before the first waiting event of a key starts, the SDK calls the hook, and the key's events stay in the queue until it settles while other keys keep starting. A hook that returns a plain value marks the key ready at once. Readiness is kept only while the key has waiting or running events, so the next burst after the key went idle calls the hook again, and hooks for different keys run at the same time, so the hook should be a cheap, idempotent check. A hook that throws, rejects or fails reports every waiting event of that key as a failure of its handler, and a later event calls the hook again. Held events count towardEvent Options max,Pending Messages maxand a draining shutdown. The option needsPending Bytes partitionand otherwise fails withConfigurationwhoseError fieldis"partition, a new member of theReady" ConfigurationunionError .field Dropped events are also counted per event name.
Clienthas the newCounters eventsmember, which counts overflow, closed and collector drops and malformed dispatches whose type names exactly one event, and theDropped By Event eventsreason totals are unchanged. Drops that have no known event name appear only in the reason totals, so the per-event sum can be lower. The metricDropped fluxerly_gains anevents_ dropped_ total eventattribute where the event name is known, next toreasonBehaviour changes to know about:
- A partitioned handler with
overflow: "drop, which is the default forOldest" clientand.on run, now drops from the longest backlog instead of the oldest waiting event overall. The backlog of the arriving event's key counts the arriving event. When another key has a longer backlog, that key loses its oldest waiting event, with ties going to the key whose oldest waiting event is oldest. Otherwise the arriving event's own key loses its oldest waiting event, or the arriving event itself when that key has none waiting, and this repeats until the arriving event fits. When the arriving event would be discarded anyway, it is the only event discarded. UnderBot maxthe choice follows the number of waiting events, not their sizes. The Warn record, counters and policy field are the same, but a handler that relied on the globally oldest event being dropped, or on every partition losing events in arrival order, sees different events dropped. PartitionedPending Bytes commandsdelivery follows the same rule. Handlers without a partition and the policiesdropandNewest stopare unchanged - The partition function now runs when an event is received, instead of when the handler first considers it, so a slow partition function runs on the gateway callback. A throwing function still reports that event's handler as failed
- A partitioned handler with
-
989089a: Command routers can now wait for a partition's own setup before its commands run. The
partitionoption works forReady routerand for the.attach commandsoption ofrunandBot create(Test Bot Bot), in both APIs, with the same rules as theCommands Options partitionoption ofReady client. Before the first waiting message of a key starts, the SDK calls the hook, and the key's messages stay in the queue without using a command slot until it settles, while other keys keep running. The option needs.on partitionand otherwise fails withConfigurationwhoseError fieldis"partition. A hook that fails reports each waiting message of that key to theReady" onoption of the commands, and those messages run no commandError Behavior change for command routers that have an
on: A failingError partitionhook, a throwingReady partitionfunction and an overflow report of the router's subscription now reach thaton, in arrival order with the command failures. Before, a throwingError partitionfunction and an overflow report went to the client-levelonor the log, so a bot that replied fromError commandsstayed silent. A router without its own.on Error onis unchangedError In the Effect API the hook returns an Effect, and its required services are added to the requirements of
runandBot create, which gain a trailing type parameter for them, andTest Bot Botgains a fifth oneCommands Options -
321d8bb: Permission and role helpers now cover cache-first reads, role rank, timeouts, partial overwrite changes and readable permission names, in both entry points. The
permissionsmethod takes.fetch prefer(Cache: true Permission) to use enabled caches for the guild, member, roles, channel and a thread's parent and fetch only what is missing, while the default stays a fresh read. TheFetch Options hierarchyand.highest Role( member, roles) hierarchyhelpers (.can Manage Role( { guild, actor, role, roles }) Role) check rank from held snapshots, andManagement Input membersreports.timeout State( member, { now }) "none","active"or"unknown"fromcommunication. TheDisabled Until permissionhelper (Bits .patch Overwrite( current, { allow, deny, inherit }) Overwrite) changes named permissions of an overwrite and keeps every other bit, andPatch permissionreturns Fluxer's English name for a permissionBits .label( name) Behaviour changes to know about:
- Missing-permission hints and the
requireguard denial now use Fluxer's labels instead of identifiers or split identifiers, such as "Manage community" instead of "Manage Guild" and "ManageGuild", "Use voice activity" instead of "Use Vad" and "Time out members" instead of "Moderate Members". Code or tests that match this text must change. ThePermissions detailsfield still holds.required Permissions Permissionskeys - The
requireguard now decides throughPermissions permissionswith.fetch prefer, so it shares one deadline for its reads. The command is denied only when the channel's permissions cannot be confirmed, meaning Fluxer sent a channel without its permission overwrite list, and the reply is "This command cannot confirm this channel's permissions", replacing "This command cannot check permissions in this kind of channel". Code or tests that match this text must change. Every other failure still fails the commandCache - A fetched community channel that is not a thread and comes without a permission overwrite list fails
permissionswith reason.fetch responseon both paths, because Fluxer sends the list as an empty list when there are no overwrites. The failure has outcome"unknown"as other unusable responses of a read do, recordsdetails,.read detailsand.channel Id details, and carries a hint. A cached channel without the list is fetched again. With.response Field prefer, a closing client now fails a cache lookup withCache ClientoutcomeClosed Error "not, as fresh reads doDispatched" - The
channelscomment now says that Fluxer requires.remove Permission Overwrite Manage, and a bot without Administrator can remove only an overwrite whose denied permissions it has all ofRoles
- Missing-permission hints and the
-
c892c2c: Handlers of an update or delete event now receive the cached value from before the change, as
previouson theEventthatContext clienthandlers,.on runhandlers and event middleware receive in both APIs, so edit and deletion logs can show what changed. TheBot message,Update messageandDelete messageevents read the message cache,Delete Bulk guildandMember Update guildthe member cache,Member Remove guildandRole Update guildthe role cache,Role Delete guild,Channel Update guild,Channel Delete threadandUpdate threadthe channel cache, andDelete guildandUpdate guildthe community cache. The snapshot is the frozen value the client's own cache held just before it applied the event, never fetched. ADelete messagesnapshot is a frozen list of the messages the cache held, once each in the order of the event'sDelete Bulk ids, because Fluxer sends only IDs for a bulk deletion. It isundefinedwhen that cache category is not enabled, the cache never saw the entity, or the entry expired or was removed, and for every other event. A write through an SDK resource method updates the cache too, so the event after it can hold the written value, orundefinedwhere the write removed the entry, instead of the value from before the write. A queued event keeps its snapshot in memory until its handler runs or the event is dropped, andmaxdoes not count it. The test clients deliver it the same way when their cache option is enabledPending Bytes The new
Previousinterface lists the snapshot type per event, and the newSnapshots Previoustype gives the type ofSnapshot<K, M> previousfor one event.Eventnow takes optional type parameters for the event and message type, andContext Event,Invocation Of .context Botand theEvent Context .context clienthandler's context are typed for their event. A helper that takes a plain.on Eventstill accepts any handler's context, including one from a client withContext message, and a context built by hand, such as in a unit test, can leaveFields previousout -
ab97759: The
ratelimitand.wait ratelimitlog records and the.deadline rateobservation now repeat what aLimit 429response sent about the limiter that answered it. Log records carry these values infields, andRatehas them as optional members:Limit Observation scope,bucket,limit,remainingandresetfrom theAfter Seconds X-Rate,Limit-Scope X-Rate,Limit-Bucket X-Rate,Limit-Limit X-RateandLimit-Remaining X-Rateheaders,Limit-Reset-After retryfrom a numericAfter Header Seconds Retry-Afterheader, andretryandAfter Seconds bodyfrom the body'sGlobal retry_andafter globalmembers. Each value is present only when the response sent it in the expected form. Aratelimitrecord for a request refused before sending, behind a known pause, has none. Waits, retries and the existing.deadline globalvalue are unchanged -
d63165f: Roles can have icons.
Guildnow hasRole icon, the image's hash, next tounicode, andEmoji assetsbuilds the image's URL..role Icon RoleandCreate RoletakeEdit icon, an image data URI, andunicode, where null removes the icon and setting one replaces the other.Emoji Rolealso takesCreate iconto copy another role's icon. The input fails locally when it sets an image together with an emoji or a role to copy from, and the everyone role accepts neither fieldSource Role Id -
cd412ec: Behavior change:
text,.truncate textand the pages of.split routernow cut at a grapheme boundary, as.help Intldefines it, so a visible character stays whole. Before, they kept the two UTF-16 units of a code point together but could cut inside a character made of several code points, such as a flag, a skin-tone emoji, a joined emoji, a base letter with combining marks, or the pair "\r\n". For example,.Segmenter textreturned.truncate( "aaaaaaaaa🇩🇪", { max Length: 11, suffix: "" }) "aaaaaaaaa🇩", half a flag, and now returns"aaaaaaaaa". A piece or truncated result can now be shorter thanmaxand never longer, and the newline and whitespace preferences ofLength textonly choose positions between characters.split textkeeps whole characters only. When the first character does not fit before the suffix, it returns the suffix alone, so.truncate textnow returns.truncate( "🇩🇪x", { max Length: 4 }) "…"instead of half a flag, and a letter with more combining marks than fit is dropped entirely.textstill cuts a single character that is longer than the limit between code points, because it must advance, and it still throws.split Helperwhen the limit cannot hold one code point. Joining the pieces ofError textstill reconstructs the input exactly, except that each lone surrogate becomes U+FFFD.split No code change is needed unless a caller relied on a cut inside a character, or on a result of exactly
maxunits of textLength - suffix .length -
419fb51: The
textand.truncate texthelpers now replace each lone surrogate with U+FFFD (�) instead of throwing.split Helper. Ordinary code can create one, for exampleError slicecan cut an emoji in half, and Fluxer refuses a message that contains one, so a bot's text helper no longer fails on it. The replacement has the same length, somaxstill holds, andLength textpieces join to.split content. A code path that relied on the error can check.to Well Formed() contentfirst. A suffix that is not well-formed still throws, and so does text that is not a string.is Well Formed() -
a47b104: The package installs a
fluxerlycommand. Runningnpx --no fluxerly agents,pnpm exec fluxerly agentsorbunx --no-install fluxerly agentsin an application's folder copies the SDK's rules for coding agents into itsAGENTS.md, which coding agents read automatically, and records the installed SDK version. Running it again replaces only that section, and a broken section marker stops the command without changing the fileThe agent guide at
agents/starts with those rules, includes the starter bot and a Result check, and lists patterns that do not exist in Fluxerly. The package's entry points name the guide in their overviewAGENTS.md -
abb5d3f: Add
channels,.follow channels,.fetch Follower Stats messagesand.publish messagesto both APIs for announcement-channel follows, follower counts, message publishing and public source-community lookups. Follow results identify the source channel and the created follower webhook, which can be deleted to stop following. After publishing, the cached source is updated and copies enter the cache only when they arrive. After an unknown outcome, the SDK drops the source from the cache and does not publish again.fetch Crosspost Source The
errorshelper now names announcement, follow, conversion, publishing and published-message editing rejections, such as.api Code invalidFollow Target Channel -
c4f4f49: Add
ownerto the result ofId clientin both entry points, the decimal user ID of the account that owns the bot's application. The result keeps no other owner details, and a response without a valid owner ID now fails with.application .fetch() Botand reasonApplication Operation Error "response".guardscalled without IDs now allows only that owner. The first invocation reads the owner with.owner Only() application, and the guard keeps the ID for the client's lifetime. A failed read keeps nothing and fails the command, and the next invocation reads again. Explicit owner IDs work as before, and an.fetch undefinedargument still throwsConfigurationError -
7733b23: A client logs one
lifecyclerecord once every shard it owns is connected, such as.connected Connected to Fluxer as My. When the bot owns every shard and is in no community, the record is a Warn that includes the bot's installation linkBot in 3 communities Loading any entry point on a Node.js version older than 24.15 throws an error that names the required and the found version, instead of failing later with an unrelated error
Several configuration errors explain the likely fix:
- An event named
ready,client,Ready onorReady connectedpoints to the connected record, thesetupoption ofrunandBot client.connect() - An unsupported command argument type suggests the supported type it most likely meant, such as
textforstring, and lists every supported type - A missing bot token mentions
node --env-file=.env bot.js, the current folder and a.envfile saved as.env.txt - A command definition placed next to
prefixin thecommandsoption ofrunis explained as belonging in the innerBot commandsobject, an unknown key there listscommandsandonamong the supported keys, and a missingError prefixis reported as required
- An event named
-
139f4dc: Add atomic keyed command batches with per-command argument inference, plus command-context reply helpers that bind the incoming message and handler cancellation
-
3d68f4d: The
clientmethod now bounds the work each listener can hold, in both APIs. A listener whose calls never finished, such as one returning a promise that never settles or an Effect that never ends, kept one unfinished call per cache change without limit. A listener now has at most.cache .on Change concurrencyunfinished calls, 256 by default, and up tomaxlater changes, also 256 by default, wait in the order they were applied. A change arriving beyond that drops the oldest waiting change, logs aPending Changes cacheWarn with the observer's new.changes Dropped idand how many changes it has dropped, and counts the drop indiagnostics(). Both limits go in the new optional second argument,.counters .cache Changes Dropped Cache, andObserver Options { concurrency: 1 }handles changes one at a time in order. A limit that is not a positive safe integer, or an unknown key, throwsConfigurationin the default API and dies with it in the Effect API. A synchronous listener finishes each call before the next change, so the limits never delay or drop its changes. Shutdown does not wait for unfinished listener calls, so changes still waiting at shutdown, including its final clears, are dropped and recorded the same wayError -
7733b23: Add
clientto observe changes to the client's local caches as.cache .on Change( listener) { kind, op, key }records, whereopisset,deleteorclear. Every cache kind reports stored and replaced entries, removals including expiry and capacity eviction, and whole-kind clears fromcache, connection gaps of unknown scope and shutdown. Keys use decimal IDs, such as.clear() channelfor messages andId:message Id guildfor members. Changes are delivered in applied order after the cache work that produced them, a failing listener is reported as a cache failure, and no change is recorded while no listener is registeredId:user Id -
3d68f4d: Each category in
clientnow keeps running totals since the client was created, in both APIs. The.diagnostics() .caches hitsandmissesvalues count local lookups such asclient, including lookups the SDK makes itself, and the.messages .get evictionsvalue counts entries the cache removed on its own, withcapacityfor room withinmaxorEntries maxandBytes expiryfor entries older thanmax. Removal by events, writes,Age Ms cache, clears, connection gaps or shutdown is not counted, and a disabled category reports zero.delete -
7755411: The
clientmethod now releases a single cache kind, and the new.cache .clear( kind) clientremoves one entry by the key format that.cache .delete( kind, key) cachereports, such as.on Change guildfor members orId:user Id channelfor messages. Both work in the default and Effect APIs, run synchronously and report aId:message Id clearordeletechange tocachelisteners. Reads already in flight cannot restore what was removed. An invalid kind or key throws.on Change Configuration, whoseError fieldiskindor the new valuekey -
7733b23: Add the
call,Create call,Update callandDelete entranceevents for Fluxer's CALL_CREATE, CALL_UPDATE, CALL_DELETE and ENTRANCE_SOUND_PLAY dispatches, with frozenSound Play Call,Create Call,Update CallandDelete Entrancepayloads. Call participants useSound Play Call, which omits Fluxer's internal voice routing fields. A malformed dispatch of these types is skipped and counted like other malformed events, without clearing any cacheVoice State -
1afd073: Preserve provider-supplied guild context as optional
guildonId message,Delete messageandDelete Bulk channelpayloads through both APIs. No channel lookup or cache inference is added, and omission does not establish that the event came from a private channelPins Update -
c4f4f49: Add
fluxerly init, which writes a JavaScript, TypeScript or Effect starter bot into the current folder. In a terminal it asks which starter to write, with JavaScript as the default. The--template js|ts|effectoption chooses without asking, and without a terminal or the option it writes the JavaScript starter. An unknown template prints the usage, writes nothing and exits with status 1. Each starter writes apackage.jsonwithstartandtestscripts that run without a build step, the bot with the starter's!pinghandler, a test that talks to the bot throughsayfrom@neontechspace/or, for Effect,fluxerly/ testing @neontechspace/,fluxerly/ effect/ testing .envand its copy.example .env, a.gitignorefor.envandnode_, and themodules AGENTS.mdthatfluxerly agentscreates. The TypeScript and Effect starters writebot.tsandbot, which Node.js runs directly, and a strict.test.ts tsconfig.jsonfor editors and acheckscript that runs the compiler. It replaces no file. When any of these files other than.envand.gitignoreexists, it lists them and writes nothing. An existing.envis kept, and an existing.gitignoregets only its missingnode_andmodules .envlines. It then installs the SDK, Effect for the Effect starter, and TypeScript 7 and the Node.js types for the TypeScript and Effect starters, with the package manager that ran it, npm, pnpm or Bun, and prints the next steps with that manager's test and start commands and, for those two starters, its type-check command. The--no-installoption skips the install and prints the install commands instead, for offline or CI use. A failed install keeps the written files, prints the install commands and exits with status 1 -
7733b23: Add the
observeclient option to export metrics and traces from either entry point to any monitoring system. The observer receives one frozenObservationfor each finished REST request attempt, with its method, route template, status, duration and attempt number, each rate-limit wait, each reconnection attempt, each resumed session, and each finished handler or command invocation, with its duration, outcome and the error name and code of a failure. Observations never contain tokens, payloads or message content, and route templates replace IDs with placeholders. The observer runs synchronously and independently of the logging settings. An observer that throws is counted indiagnostics()without changing SDK work. The Effect API keeps its Effect metrics and spans and calls the same observer.counters .sink Failures -
cae766a: The new
clientmethod runs a task once after.schedule delayor repeatedly withMs intervalbetween runs, owned by the client's lifetime in both APIs, so aMs runcommand, handler orBot setupcan start delayed or repeating work without keeping a command slot busy or leaving a timer behind. It returns aScheduledwhoseTask close()cancels later runs. Thedelayvalue is 0 through 2,147,483,647 andMs interval1 through 2,147,483,647, and a repeating task first runs afterMs intervalunlessMs delayis set. Invalid options throwMs Configurationin the default API and die with it in the Effect API. The default API takes a function that receives a cancellation signal, and the Effect API takes an Effect that keeps the caller's services and gets a Scope of its own for each run. Each failed run is reported to the client-levelError on, or logged at Error with the new codeError lifecycleunder the new failure kind.task Failed task, and a repeating task keeps its schedule after a failure. A draining shutdown, including a requestedrunstop, lets running tasks finish withinBot drainand counts them in the drain records' newMs tasksfield, and every shutdown cancels the rest, counting the tasks it cut short in onelifecycleInfo record. The test kit's.tasks Cancelled idle()andsayin both APIs also wait for scheduled task runs in progress, while a task still waiting for its time does not hold them up. Scheduling after shutdown began throwsClientin the default API and dies with it in the Effect API. The test clients treat a failed task run without anClosed Error onhook as an unhandled failure. Tasks are kept in memory only. The commands guide's reminder example now schedules its reply instead of waiting inside the command, and the bot lifetime guide explains scheduled tasksError -
7733b23: Command contexts in both entry points gain
help(, which builds help pages from the router that matched the command, so aoptions?) helpcommand works in the keyedcommandsobject ofrunwithout a reference to the router. The prefix defaults to the one the invoking message used and the page length to 2,000 UTF-16 code units. The other settings matchBot router, and the new.help Commandtype describes them. For example:Context Help Options help: { execute: ({ help, reply }) => reply( help()[0] ?? "No commands") } -
ce65b67: The
clientmethod now accepts.logging .configure debugin both APIs, so Debug output can be changed while the client runs, including output turned on byFLUXERLY_. ADEBUG debugvalue replaces the current Debug categories:truefor every category, a list for those categories, andfalseor an empty list for none. An omitteddebugkeeps the current categories, while an omittedlevelorcategoriesstill returns to its default.Loggains theLevel Settings debugsetting -
7733b23: Make errors and log records say what went wrong and what is accepted.
HelperandError Assetmessages name the helper and the accepted form, for exampleUrl Error Helper operation format, instead of.user Mention failed: I Ds must be decimal strings from 0 through 9,223,372,036,854,775,807 Invalid input for format..user Mention Paginationmessages include the input explanation or what stopped the iteration, and the error carries aError hint: The rejected path for invalid settings, raisingmaxforPages page, and retrying later forLimit cursorandStalled indexingInput validation explanations name the field in words and state the accepted range with its unit, such as
ms,bytesorseconds. Presence explanations name the field they describe, unsupported-field explanations list the accepted fields or the type that defines them, timeouts state theirmsunit, and ban and timeout durations state their range inmswith the matching days or years. Connection, startup and shutdown records use full sentences, such asShard 1 lost its connection. Close code 4000 (, and configuration errors name the full option path, such asUNKNOWN_ ERROR): The gateway reported an unspecified error. Reconnecting and resuming the session in 486 ms connection..startup Timeout Ms SupervisorandError Supervisormessages describe the failure and carry a hint. An instance discovery failure keeps its transport error code and says what was wrong with the response.Child Error Cancelledaccepts an optional operation, which is recorded asError detailsand named in the message, and its hint explains that signals the SDK passes to handlers abort when the client shuts down.operation The
inputandValidation .path constraintvalues are unchanged. Messages remain changeable text, so match oncode,reason,fieldandinputand on log record codes insteadValidation -
7733b23: Provide a native client as an Effect service with
Fluxerfrom the Effect entry point.Client Fluxercreates the client in the layer's scope and shuts it down when that scope closes.Client .layer( options) Fluxerreads the token, and optionally any other top-level setting, from EffectClient .layer Config( { token: Config .Redacted( "FLUXER_ BOT_ TOKEN") }) Config. The token stays redacted until the client receives it. A missing Config value fails the layer withConfig, while a blank token or another invalid setting dies withError Configuration. Building either layer does not connect. Register handlers, then callError clientor.connect() client.run() -
7733b23: Add
FluxertoTest Client @neontechspace/for applications that read their client from thefluxerly/ effect/ testing Fluxerservice.Client Fluxercreates a test client withTest Client .layer( options) createand provides its client asTest Client Fluxerand the whole test client, with its controls, asClient Fluxer. Application code then runs against the in-memory Fluxer unchanged. Closing the layer's Scope shuts the client down and dies withTest Client Unhandledfor a handler failure the test did not readTest Failures Error -
7733b23: Read the community that owns a custom emoji or sticker with
emojisand.fetch Source( id) stickersin both entry points. Fluxer calls a community a guild. The result is a frozen.fetch Source( id) Expressionwith the community'sSource Guild id,name,iconhash and badgefeatures. Fluxer answers when the source community is discoverable or the bot is a member of it. A private or unavailable source community fails withGuildwhoseOperation Error apiisError .code unknown. Badge names added by Fluxer later are kept rather than failing the read, and a response missing any documented field fails with reasonResource response -
7733b23: The
membersmethod accepts.fetch Can Manage actorto check whether another member, such as the moderator who ran a command, outranks the target instead of the bot. It reads that member in place of the bot's own membership, and an invalid ID fails with reasonUser Id inputbefore any request. Both entry points name the option typeCanManage Options -
314ac29: Add
fileto both entry points, a built-inSession Store( directory) SessionforStore shardingthat keeps one JSON file per shard. Saves write a temporary file with mode 0600, flush it and rename it over the shard's file, so a crash never leaves a partial snapshot. A missing file loads as no snapshot, and a corrupt file rejects without quoting its contents, so the session ID never appears in logs.sessions -
abb5d3f: Add the
guildevent with a frozenHealth Update Guildpayload,Health Update { guild, in both entry points. Fluxer sends it when the server hosting a community starts or stops lagging. It does not mean that the bot is disconnected or that the community is unavailable, and the SDK neither reconnects nor clears caches because of itId, degraded } -
1afd073: Add
Guildto distinguish provider-reported joins from startup and recovery availability snapshots through both APIs. ExistingCreate .is New Join guilddelivery andCreate connect()readiness remain unchangedClassification requires a bot gateway that supplies Fluxer's availability marker. Legacy instances that omit it for startup snapshots cannot be classified reliably, and a join dispatch may replay during Resume
-
7733b23: Recognize Fluxer's
TWO_rejection in both entry points. A kick, ban, timeout or other action that needs a moderation permission in a community that requires two-factor authentication now fails withFACTOR_ REQUIRED apiset toError .code twoandFactor Required apiset toError .provider Code TWO_, instead ofFACTOR_ REQUIRED apiset toError null. Fluxer returns it when the account that owns the bot's application has no two-factor authentication enabled and the bot does not own the community. Communities, which Fluxer calls guilds, gain an optionalmfafield with the newLevel GuildconstantsMfa Levels NoneandElevated, so a bot can see this requirement before it sends a moderation request. The field appears on fetched communities, community list entries andguildandCreate guildevents. A community whoseUpdate mfa_is present but not 0 or 1 is treated as malformed, like other malformed community fieldslevel The moderation and message deletion references describe this requirement, and the
messagesreference states that it requires.delete Many Manageeven for the bot's own messagesMessages -
7733b23: Add
snowflakes, which returns the shard Fluxer routes a community's events to, and.shard For( guild Id, total Shards) client, which returns this client's shard for a community or.shard Id For Guild( guild Id) undefinedwhen another process owns it. Fluxer calls a community a guild. Both APIs provide themFor an invalid ID, the helper throws
Helperwith reasonError id, and for a total outside 1 through 16,384 with the new reasonshard. The client method throwsCount Configurationfor an invalid IDError -
7733b23: Pass an
EventwithContext shardandId receivedto everyBytes clienthandler, as the third argument in the default API and the second in the Effect API. The context names the local shard that received the event and the byte length of the gateway frame that carried it.on Add
clientto run event middleware around every later.use( middleware) onhandler invocation, including those of existing subscriptions, command routers andrunhandlers. Middleware runs in registration order, receives the event name, payload, context and subscription ID, and can skip the handler by not callingBot next. Failures of middleware and of the handler inside it are reported like handler failures, so middleware cannot hide a handler failure. Both APIs return aMiddlewarewithRegistration close(). The default registration also works withusing, and the Effect API ends the registration when the executing Scope closes and runs middleware with the services available at registration -
7733b23: Add the
partitionhandler option toclientand.on runevents, in both entry points, to keep related events in order while unrelated events run side by side. Events with the same key run one at a time in receive order, and events with different keys run in parallel up toBot concurrency, which defaults to 8 when a partition is set. The value"guild"keys each event by its community, which the API calls a guild, and a direct message by its channel. The value"channel"keys each event by its channel, or by its community when it has no channel. A function receives the event and returns its own key. Waiting events still count toward the queue limits. A partition function that throws or returns a value other than a string or undefined is reported as a failure of that event's handler, which then does not run -
7733b23: Add
gateway,.ignored Events gatewayand.flags gateway, sent in each new session's Identify. The.presence ignoredoption lists dispatch names Fluxer should not send, orEvents "auto"to suppress every type no registered event, enabled cache category or SDK feature needs, computed at each new session. A handler registered after a session started receives suppressed types only from that shard's next new session, and arawsubscriber disables automatic suppression. Settingflags: { debounceturns on Fluxer'sMessage Reactions: true } DEBOUNCE_flag, merging direct-message reaction runs intoMESSAGE_ REACTIONS message. TheReaction Add Many presenceoption sets the initial presence, which Identify carries and the SDK publishes after READY as ifpresencehad been called before connecting. An explicit.set ignoredlist that names a type the SDK or an enabled cache needs fails client creation withEvents Configuration.Error Gatewayis a new public typeOptions -
7733b23: Expose the instance's advertised web domain migration as
domainonMigration Resolved, typed as the newInstance Instance, and log an Info record when a migration is enabledDomain Migration -
314ac29: Add
client, which changes a running client's log level and per-category levels with the same validation as the creation settings. Omitted settings return to their defaults, and the.logging .configure( { level, categories }) debugsetting andFLUXERLY_still apply. The Effect API has the same member, because its clients apply these levels before records reach the Effect logger and the two clients keep the same members. The newDEBUG Logtype describes the accepted settingsLevel Settings -
abb5d3f:
messages,.delete messages,.delete Many messagesand.pin messagesaccept an.unpin auditoption in both entry points, which Fluxer now records in the community's audit logReason -
7733b23: Message operations accept a plain string as the whole message and an
Embedwherever they accept embeds, in both entry points. A string such asBuilder clientis shorthand for.messages .send( channel Id, "Hello") { content: "Hello" }inmessages,.send messages,.reply messages,.edit directand a webhook client'sMessages .send sendandedit. AnMessage EmbedinBuilder embedsis built once when the operation reads its input, soclientneeds no.messages .reply( message, { embeds: [builders .embed() .title( "Status")] }) build()call and later changes to the builder do not affect that operation -
7733b23: Send text-to-speech messages with
tts: trueonmessages,.send messages,.reply directand theMessages .send runreply helpers. A non-boolean value fails with reasonBot inputbefore any request. In a community, Fluxer sends a normal message without an error when the bot lacks the Send TTS Messages permission. Webhook messages do not accepttts, because Fluxer never sends a webhook message as text-to-speech. Webhook sends, replies and forwards withttsfail with reasoninputbefore any request. Messages gain an optionalttsfield, selectable throughmessage. Fluxer does not store the value, so it is meaningful only on the message returned by a send and onFields messageevents. Fetches, history pages and other later reads always reportCreate false. A message whosettsis present but not a boolean is treated as malformed, like other malformed message fields -
7733b23: Add
Messageconstants to both entry points, naming the values ofType Message, for example.type message. The constants are.type === Message Type .User Join Default,Recipient,Add Recipient,Remove Call,Channel,Name Change Channel,Icon Change Channel,Pinned Message User,Join Channel,Follow Add Thread,Created ReplyandThread.Starter Message Messagestays one shape rather than a union by type, because Fluxer returns the same message fields for every type. A value Fluxer adds later is still kept inMessage.type -
d3a22bc: A missing-permission rejection now names the permissions the failed operation needs, in both entry points. When Fluxer answers HTTP
403withMISSING_, guild, channel, message, send and webhook operation errors list them inPERMISSIONS detailsas.required Permissions Permissionskeys, such as["View, and the hint names them, as in "The bot needs View Channel and Manage Channels for this operation". The hint also says where to grant them and adds other checks behind the same rejection, such as role hierarchy for moderation or Manage Roles when a channel edit changes permission overwrites. Fluxer does not report which permission is missing, so the bot may already have some of the listed ones. The change covers 72 operations, including channel, webhook, invite, role, member moderation, message, pin and reaction operations. Operations whose checks depend on data the SDK does not have, such as editing or deleting an emoji or sticker, andChannel", "Manage Channels"] restkeep a general hint without.request details. Operations without an entry that previously received the generic message-sending or moderation hint now receive the general hint instead.required Permissions -
7ec55f6:
Presence, and so each entry ofUpdate Presenceand of the presences in a member chunk, now carriesUpdate Bulk customin both entry points, instead of dropping the custom status Fluxer sends. The value isStatus nullwhen the account has none, which includes an offline or invisible account because Fluxer hides the status of one, and otherwise anObservedwithCustom Status text,emojiandexpires. TheAt emojifield isnullor anObservedwithCustom Status Emoji id,nameandanimated, so a custom emoji keeps its ID and animation flag and a Unicode emoji has a name and no ID and always reads asanimated: false. Fluxer stores custom statuses without validating them, so a field of the wrong type reads asnulland the presence is still delivered -
7733b23: Add the
rawevent, which delivers every gateway dispatch a shard accepts as{ shard, includingId, t, s, d } READY,RESUMEDand types this SDK version does not decode. The body is Fluxer's unvalidated wire data and is not part of the SDK's compatibility contract. Delivery is logged at Trace with codeevents, and nothing is built while no.raw rawsubscriber exists. Types listed ingatewaynever arrive.ignored Events Add
clientfor gateway commands without an SDK method. It fails with.gateway .send( shard Id, op, d) GatewaywhoseSend Error reasonisinputfor invalid arguments or frames above Fluxer's 4,096-byte limit,reservedfor heartbeat, Identify, Resume and server-only opcodes,not,Owned notorReady busy, and withClientafter shutdown. A throw from a getter orClosed Error toin the command data rejects withJSON SdkcodeDefect application, and cycles, BigInt values and overly deep data fail with reason.defect input. Completion means the frame was handed to the connection. Each sent command is logged at Debug with codegateway.command Sent -
34b3ab0: Reaction pages show several pages in one message that people flip with ◀ and ▶ reactions, because Fluxer has no buttons. Call
ctxin a prefix command, where only the command's author can flip by default, or.paginate( pages) clientelsewhere, in both the default and Effect APIs. Pages are strings or.messages .paginate( channel Id, pages, { users }) { content, embeds }objects. Adding or removing an arrow both count as a click, so the bot needs only Add Reactions and Read Message History, andremoveinstead removes each click, which needs Manage Messages. Listening stops afterClicks: true idle(default 60,000) without a click and at most afterMs timeout(default 300,000), then the bot removes its own arrows and the current page stays. The result reportsMs idle,timeoutorsingle, and failures and cancellation use the usual failure channel. The newPage ctxsends the router's help as one message, or as reaction pages when it needs more than one page. So that pages started after connecting receive clicks,.send Help() gatewaynow always keeps.ignored Events: "auto" MESSAGE_andREACTION_ ADD MESSAGE_REACTION_ REMOVE -
7733b23: Export the types that public signatures already referenced, so they can be named in application code and appear in the reference. Both entry points now export
Format,Helpers Snowflake,Helpers Display,Helpers Permission,Bit Helpers Color,Helpers Text,Helpers LinkandHelpers Assetfor the helper objects, whoseHelpers trymethods return a Result in the default entry point and an Effect in the Effect entry point, andParse Operation,Message,Content Edit,Message Options Webhook,Message Options Audit,Log Filter Audit,Log Query Base Audit,Log Iteration Query Base CommandandArgument Optional Value Error. Both entry points addMatch Handlers CommandsandPrefix, each with its own API's shape, and the Effect entry point addsCommand Batch Batch,Requirements Command,Requirements Effect,Requirements BotandEvent Services HandlerServices Every public export now has a reference category, and each client method has one description shared by both entry points, with the differences between them stated separately
-
7755411: Every resource cache kind now accepts a
maxcallback, as theAge Ms messagescache already did, so an application can choose per community, channel or other snapshot what gets cached. The callback receives the frozen snapshot and returns a duration in milliseconds,nullfor no age limit or0to skip retention, for examplechannels: { max. A callback that throws or returns an invalid value removes the older copy and is reported as aAge Ms: ( channel) => ( watched .has( channel .guild Id) ? null : 0) } cachefailure to the client-levelon, or logged at Error, without changing the event or REST result.Error Resourcenow takes the snapshot type as a type parameter, andCache Settings Clientpasses the matching type for each kindOptions .cache -
7733b23: Add
clientfor Fluxer API routes without an SDK method. Default calls return a.rest .request( { method, path, query?, body?, files?, audit Reason?, timeout Ms? }) Resultand acceptAsync signal, and Effect calls return an Effect. Both resolve with{ status, headers, body }, where header names are lower case andbodyis the parsed JSON body orundefinedfor an empty response. Requests use the client's credential, queue, learned rate-limit buckets, 429 waits, deadline, logging and bounded GET retries. Other methods are sent again only after a confirmed 429, never after an uncertain outcome. Paths must be relative API paths such as/users/@me, without a scheme, host, query, fragment, dot segments or the/v1prefix. Token-authenticated webhook routes are rejected because their credential is part of the path. Invalid input fails withRestreasonRequest Error inputbefore any request, a non-2xx status fails withRestcarrying the status and sanitizedRequest Error api, and a request after shutdown fails withError Client. Logs, metrics and spans show only a route template in which IDs, codes and other non-word segments are placeholders. TheClosed Error filesfield accepts the message attachment inputs and sends them inline as multipart form data with their metadata inpayload_json -
7733b23: Add the
restclient option to tune REST scheduling: Theconcurrencysetting for Fluxer API requests that run at once (1 through 64),mediafor attachment downloads (1 through 64, default 4),Concurrency max(1 through 65,536, default 256),Queued queued(64 KiB through 256 MiB, default 4 MiB) andJson Max Bytes default(default 30,000). By default a client runs 4 API requests at once for each of its shards, up to 64, instead of 4 in total, and the limit follows the shard plan whenTimeout Ms sharding: "auto"sizes or enlarges it. An explicitconcurrencyis used as given. Thedefaultsetting replaces the fixed 30,000 ms deadline of REST operations and attachment downloads that omitTimeout Ms timeout. Invalid values throwMs Configurationnaming the field in the default API and are defects in the Effect API, andError clientreports the configured capacities.diagnostics() .rest A request that fails with reason
busybecause the REST queue or the upload byte budget is full now logsrestat Warn, at most once a minute, naming the full limit. Every such failure is counted in the new.busy diagnostics()counter.counters .rest Busy -
7733b23: A shard that resumes a session from the
shardingstore now refills the enabled community, role and channel caches through REST, because a resumed session receives no community data. Fluxer calls a community a guild. Once every shard is ready, the SDK lists the bot's communities and fetches each one on a resumed shard, one request at a time, so application requests keep the other REST slots and rate limits apply as usual. The refill is logged with code.sessions lifecycle, at Info when it succeeds and at Warn when some communities fail or it stops after 10 failures.cache Refill Set
shardingto.refill Caches falseto skip the refill -
37612fd: The
runBot commandsoption now accepts the delivery settings of the router'sattachin both entry points:concurrency,partition,overflow,maxandPending Messages max. They reach the router unchanged and are checked before any client exists, so an invalid value throwsPending Bytes Configurationin the default API and dies with it in the Effect API. PreviouslyError runpassed onlyBot on, so its commands always ran eight at a time and dropped the oldest waiting message when 256 were waiting. A command router set toError overflow: "stop"stops the bot when its queue fills, like a handler. The commands guide shows the settings -
dd52acd: The default API's
runpasses itsBot setupcallback a second argument,{ signal }. TheAbortaborts 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 clear timers and end other background work started inSignal setupwhen it aborts, so that work ends with the bot. The runner does not track or await such work. In the defaultcreate, the signal aborts when the test bot's shutdown startsTest Bot The Effect API's
setupis unchanged. It already runs in the bot's Scope, so its finalizers run and fibers it forks into that Scope are interrupted when the bot stops -
7733b23: Add scaling options for large bots. The setting
sharding: "auto", ortotal, counts the bot's communities at the first connect and uses one shard per 2,000 communities. Fluxer calls a community a guild. Fluxer rejects a new session with close codeShards: "auto" 4011(sharding required) when one shard holds more than 2,500 communities, and community IDs do not split evenly across shards, so the lower target keeps every shard below that ceiling and leaves room to grow. Counting the communities has its ownconnectiondeadline, so a slow count does not shorten the time the shards get to connect. Until the count completes,.startup Timeout Ms clientis empty. Automatic sizing assigns every shard to the client, so processes that split the shards need an explicit total. When the bot outgrows its plan while the client runs and Fluxer closes a shard with close code.shards 4011, the client keeps running. The SDK counts the communities again and moves every shard to the plan for the new count, or to one more shard when the count still fits the old total. Every shard then starts a new session under the new total, so events sent during the move are missed, and the community caches refill from the new sessions. The move is logged once at Warn with codelifecycleand the old and new totals. After 3 moves within an hour, or at 16,384 shards, a further.resharded 4011closure ends the client with aConnection, and theError lifecyclerecord says why. An explicit.connection Ended totalstill ends the client on the firstShards 4011closure. An application can passshardingto coordinate new-session Identify commands across processes, and a refused permit is logged at Error and retried like a connection failure. The coordinator owns Identify pacing, so the SDK adds no spacing of its own before a permit. A coordinator should space its permits, for example by one second, to stay within Fluxer's budget of 300 Identify commands per source IP address in each 60-second window. A.identify .permit( shard Id, total Shards, signal) shardingstore loads a saved.sessions { sessionsnapshot at startup and saves one at shutdown, so a restart within Fluxer's 60-second window resumes instead of starting a new session. Stale, malformed or foreign snapshots, snapshots saved under a different shard total and store failures are logged and never fail startup. TheId, sequence, resume Url, saved At, total Shards } connectionoption sets.recovery min,Delay Ms max,Delay Ms attemptandTimeout Ms healthyfor established-session recovery, with the previous 1,000, 30,000, 30,000 and 60,000 ms as defaults, and the Effect API also accepts aReset Ms schedulethat decides recovery delays and ends recovery when it completes.Connection,Recovery Options Identify,Coordinator SessionandStore Sessionare new public typesSnapshot -
ce65b67: Events that shutdown or a closed subscription discards are now logged instead of disappearing without a trace. When the client closes, each subscription that still had waiting events logs one
eventsInfo record with the count in.discarded fields. Closing a subscription with waiting events logs the same record at Debug. A draining shutdown that received events for open subscriptions or collectors logs one.discarded eventsInfo record with the count in.refused fields. All of these events are also counted in the new.refused diagnostics()counter. A subscription stopped by.counters .events Dropped .closed overflow: "stop"keeps its existingeventsrecord.overflow -
ada1d09: Add an optional
runrunner to both SDK entry points for critical subscriptions, graceful shutdown and opt-in process signal handlingBot Configure event handlers in one object, with automatic subscription registration, access to the full client and a message reply helper. Keep the low-level APIs for custom lifetime and subscription control
Keep native Effect context and Cause information while the default API returns expected failures as ResultAsync and rejects with
Sdkfor defectsDefect -
7733b23: The
tokenoption ofsupervisoraccepts.child .run string | undefinedin both entry points, ascreateandClient rundo, soBot processcan be passed directly. A missing or blank token still fails.env .FLUXER_ BOT_ TOKEN childwith.run Configurationonce the assignment arrives, and the parent then sees that child fail during configurationError -
7733b23: Add scaling options to the supervisor in both APIs. As an alternative to hand-written
assignments, the newprocessesandshardsoptions split every shard into contiguous blocks, one per child, namedPer Process process-0,process-1and so on. Withtotalthe supervisor counts the communities the bot is in, which the API calls guilds, when it starts and uses one shard per 2,000 communities, as a client withShards: "auto" sharding: "auto"does. The count needs the newtokensupervisor option and acceptsinstanceandtransportfor a self-hosted instance or a proxy. It logssupervisor, and a failed count fails.automatic Sharding startwith the newSupervisorreasonError shard, whose cause is the count's error. When Fluxer later closes a child's shard with close codeCount 4011because the bot outgrew the plan, the supervisor stops every child, counts again and starts children for a larger plan, logging asupervisorWarn. After 3 such moves within an hour, or at 16,384 shards, it fails with reason.resharded closedinstead. Theidentifysupervisor option accepts the same.coordinator Identifya client accepts inCoordinator sharding, so supervisors on several hosts can share Fluxer's Identify budget. The parent asks it before every grant and adds no spacing of its own, and a refused permit is logged at Error with code.identify supervisorwhile the child retries like after a network failure. Supervised children can keep resumable sessions across a deploy with.identify Permit Failed clientinOptions .sharding .sessions supervisor. A shard plan or identify coordinator in that.child .run shardingobject is rejected withConfiguration, because the parent assigns shards and paces Identify. Each child now sends its client diagnostics (gateway latency, shard states, REST queue, cache use and counters) to the parent when it finishes configuration and then everyError diagnostics, 5,000 ms by default. TheInterval Ms status()method shows the latest snapshot in the newdiagnosticsfield of each child.SupervisorandChild Diagnostics Supervisorare new public types.Child Sharding Configurationcan name the new fieldsError processes,shardsandPer Process diagnostics, and code that switches over everyInterval Ms Supervisorreason needs a case forError shardCount -
314ac29: Add
say(to the test bot of both testing entry points. It delivers a message from a human user, waits until the bot settles ascontent, options?) idledoes, and returns the messages the bot sent in response in wire shape, or an empty list when it sent nothing. The defaultcreatenow returns the newTest Bot Testtype, and the newBot Testtype adds message overrides to the wait optionsSay Options -
7733b23: Add
@neontechspace/andfluxerly/ testing @neontechspace/for application tests that need no network, token or Fluxer account. Thefluxerly/ effect/ testing createfunction returns a real client wired through theTest Client transportoption to an in-memory Fluxer, together with controls: Theready()control connects through a protocol-v1 fake gateway,emit(delivers a wire dispatch such astype, payload, { shard Id }) MESSAGE_through the SDK's own decoders, caches and handlers, andCREATE disconnect(closes a shard's connection so the client resumes, or stops for a fatal close code. The{ shard Id, code }) userandheartbeatsettings choose the bot account reported in READY and the HELLO heartbeat interval. TheInterval Ms restcontrol answers HTTP requests matched by a.respond( matcher, response | handler) "METHOD /pattern, a RegExp or apath/:param" { method, path }object,requests()andcommands()record what the client sent without its token or Authorization header,logs()captures log records at the configured level while still calling application sinks, andcounters()reads the diagnostics counters. Message sends and edits without a matchingrestregistration receive an echoed bot message. Other unmatched requests receive a Fluxer-shaped 404 and a.respond testingWarn record. Register an explicit failure response to test a rejected message send or edit. The native.unmatched Request createis scoped: Closing its Scope shuts the client down and closes the transport, while the default client usesTest Client shutdown()orawait using. Both entry points sharefixtures,createandFixtures fixture: Deterministic snake_case wire builders for users, the bot user, communities, GUILD_CREATE bodies, channels, roles, members and messages whose defaults refer to one community, channel and authorToken The
createfunction tests a bot written forTest Bot runas written. It takes the bot's own options object, registers itsBot eventsandcommandswith the same handler contexts, delivery defaults and command router asrun, and runsBot setuponce inreadybefore connecting. Thesignal,process,Signals reportandFailure drainsettings are accepted and ignored, and an undefinedMs tokenuses the fixture token. A misspelled or unsupported option key throwsConfigurationwith a hint naming the closest key thatError createorTest Client createaccepts, asTest Bot createdoes. A failed setup rejectsClient readywithApplication. The result is the usual test client, typedError Testin the Effect entry pointBot Tests wait without building their own promises or sleeping. The
Testcontrol returns the next request a registered response answered, in order, including one that arrived before the call. TheRoute .next() idle()control waits until no handler or command is running and no request is pending, which confirms that the bot did nothing after an emitted event. Both fail withTestafterTimeout Error timeoutwhen it is passed and otherwise wait without a deadline, and the native entry point returns them as Effects. Their timers stay real when a test fakes timers, for example withMs vi. The SDK runs on.use Fake Timers() set, so fake timers must leave it real, andImmediate idle()rejects withConfiguration, or dies with it in the native entry point, instead of settling while no handler can runError A test client fails the test when application code failed without a handler, such as an event handler or command that threw, or returned a failed Result such as a rejected reply, while no
onhook was registered. The defaultError shutdown()andawait usingdisposal reject withUnhandled, which lists the Error records of those failures. The nativeTest Failures Error shutdown()fails with it, and closing the scope that created the test client dies with it. Each failure is reported once, so a later shutdown succeeds. A test that makes application code fail on purpose reads the failure from thefailures()control, which marks it as expected, or handles it withonError -
7733b23: Add the advanced
transportclient option. Itsfetchandwebsettings replace the HTTP and WebSocket implementations, for example for proxies, instrumentation or tests, andSocket userreplaces the User-Agent header. Replacements receive every client request, including instance discovery, REST, signed upload parts, attachment downloads and gateway connections. Invalid values throwAgent Configurationnaming the field in the default API and are defects in the Effect API. Bot, webhook and OAuth clients now sendError User-Agent: Fluxerlywith every HTTP request and gateway handshake. A configured.js/<version> ( +https:// fluxerly .neontechspace .com) transportreplaces it for a bot client. Custom WebSocket factories receive the handshake headers, including the User-Agent, in.user Agent WebSocket Options .headers -
1eeb268: A client created without
shardingsettings now keeps running when the bot outgrows one shard. Fluxer refuses a new session with close code4011(sharding required) once one shard would hold more than 2,500 communities, and such a client previously ended with aConnection, so a restart or a lost session took the bot down until its configuration changed. The client now counts the bot's communities and moves to a larger plan asError sharding: "auto"does, with the same limit of 3 moves within an hour, and logslifecycleat Warn with the old and new totals. Every shard starts a new session for the move, so events sent during it are missed. While the bot stays above the limit, each start first opens a session that Fluxer refuses, which.resharded sharding: "auto"avoids. An explicittotal, including 1, still ends the client onShards 4011, and supervised children are unchanged because the supervisor always assigns an explicit plan -
dd52acd: Add
clientto both entry points, a local lookup of the bot's own public account as the gateway's READY reported it, without a request. It returns the.users .get Self() Userdirectly in the default API and a never-failing Effect in the Effect API, likeusers. The result is undefined until the client's first READY, and also while no READY has carried a complete account. A later READY replaces it only when it carries a complete account, and the value remains available after the client stops. Other user updates do not change it, so use.get usersfor a current remote snapshot.fetch Self -
2b86017:
MessagegainsFlags Voice(8192), which marks a voice message in Fluxer. Message and webhook send and edit operations still reject this bit before dispatch, because onlyMessage SuppressandEmbeds Suppressare writable in this SDKNotifications -
a4c3572:
Voice, whichState voiceandState Update voicedeliver, now reportsState Snapshot is,Self Video On is,Self Streaming viewerandStream Keys member. The first three also appear onCall, whileVoice State memberdoes not, because Fluxer attaches no community member in a private call. Thememberfield is the sameGuildshape that member events use and is omitted when Fluxer sends none. An omitted video or stream flag reads as false and omitted watched streams read as an empty list, so events from servers that do not send them still arrive. A voice state whose member data is present but malformed is rejected whole, like any other malformed fieldMember -
abb5d3f: Webhook message sends, replies and forwards accept an optional
noncein both entry points. For five minutes, Fluxer then tries to suppress another send from the same webhook with the same nonce, which helps after a lost response but does not guarantee exactly-once delivery. Webhook sends without a nonce send none, and a forward that sets a nonce both on the message and on its source fails before the request
Patch Changes
-
86695d5: The shipped agent guide in
agents/now maps common bot tasks, such as sending messages, moderating members, checking permissions and testing, to the declaration file and members that cover them, instead of pointing agents at the re-export fileAGENTS.md dist/. It sends agents who need the website to its task index and method-by-task list first, and explains how to tell whether those pages match the installed version. The sectionindex .d.ts fluxerly agentswrites names the installed package's manifest as the place to compare versions and mentions the task map -
d67c5b3: API comments now state several limits precisely. By default a client runs four REST or upload requests per local shard, up to 64, and four attachment downloads, and the
restand.concurrency restoptions change these numbers. Effect API users, direct messages, webhooks and application calls use the client's.media Concurrency rest, not a fixed 30 seconds. The.default Timeout Ms usersand.fetch Self directcomments explain when a result is not cached, and theMessages .open usersand.fetch directcomments state the 256-ID exceptionMessages .fetch -
5e98527: A native
on,messagesor.collect messagesregistered in an already closed scope closes at once again. With Effect 4.0.6 or newer, the subscription or collector stayed open and client shutdown waited for it forever.collect Reactions -
a89dc6d: Contain accidentally returned Promise and thenable rejections in the default API's Effect logger adapter and in structured logging callbacks, without changing SDK operation outcomes or adding asynchronous delivery or flushing guarantees
-
38d41bf: The
messagescomments now describe Fluxer's wider deletion. Called with a channel ID, it also deletes the bot's messages in every thread under that channel, active or archived, and for a forum or media channel it deletes the bot's messages in its posts. Called with a thread ID, it still clears only that thread.delete Own Messages -
8b70a5c: The missing-permission hint for
messagesnow says that only a new reaction with a custom emoji from another community needs Use external emoji, matching Fluxer, which no longer checks that permission for the community's own emoji.add Reaction -
c44e7bc: Member avatar and banner inputs and group direct message icons now accept an image data URI with parameters such as
;charset=utf-8, as community icons already did. Before, the SDK rejected them locally although Fluxer accepts them -
0f8a442: The
.envfile thatfluxerly initcreates for the bot token is now readable only by its owner on Linux andmac, with mode 0600, so other local accounts cannot read the token. An existingOS .envkeeps its content and permissions -
408fafb: Request text with a lone surrogate, half of a character such as an emoji left by
slice, now fails locally with reasoninputand a message that explains the fix. Before, Fluxer answered it with an HTTP503error page and stored nothing, which looked like an outage. This covers every request the SDK encodes, such as message content, embeds and channel topics. Thetextand.truncate texthelpers and.split toclean such text, andWell Formed() clientstill sends its body as given.rest .request -
051a969: The
memberscomments now explain a refusal Fluxer added. Moving a member into an occupied voice channel whose participants all use peer-to-peer voice leaves the member in its current channel and fails with reason.move rejected, HTTP400anddetails.provider Code USER_, even though the member is in voice. A retry fails the same way while the destination stays in peer-to-peer modeNOT_ IN_ VOICE -
e831234: The Effect API
supervisorhelper now succeeds when the parent stops the child before its gateway session is ready, as the default API helper already did. It previously failed with.child .run Clientafter that normal stopClosed Error -
a89dc6d: Releases now come in two kinds, Nightly and Stable, and the README's install advice says so. Every Stable ships the same code as a published nightly.
-
8e2a652: A nightly is an automatic build of the latest source, published under the
nightlynpm tag twice a day when the SDK changed, with a version such as1000.0.0-nightly.20261019.57, which carries the UTC build date and the run number. Run numbers only grow but can skip values, while the core of a nightly version can drop when a pending major or minor note is removed. A nightly can include breaking changes, so install it with--save-exact, or--exactwith Bun. -
08747b6: The
restWarn record logged for a refused permission overwrite write now carries a precise.rejected fields. Before, a 403.hint MISSING_on setting or removing a channel's permission overwrite logged the general missing-permission hint. Now the hint is the one thatPERMISSIONS channelsand.set Permission Overwrite channelsalready put on their errors: It names Manage roles, where the channel's permission overrides title it "Manage permissions", the rule that the bot can allow, or stop denying, only permissions it has in the channel (for a removal, that it needs every permission the overwrite denies), and the.remove Permission Overwrite permissionhelper. No code change is neededBits .refused Grants -
e5e7d10: Fixes for the guild cache, command replies, retry classification and message cleanup. A successful
discovery,.apply discoveryor.edit guildscall no longer stores its reply as a cached guild, so.edit Vanity Url guildsand guild enumeration no longer return an application or vanity object after one of these writes. These writes now only remove the cached guild.get The
Messagefrom aCleanup Error previewwhosefilterthrew now keeps the thrown value ascause, as documentedWith
mention, help pages and the usage line of a rejection reply now put one space between the bot mention and the command name, such asPrefix <@99> say <text...>instead of<@99>say <text...>. A cooldown reply now gives a wait over 60 seconds in minutes, hours or days, with at most two units rounded up, such as "Try again in 1 hour 1 minute" or "Try again in 1 day 1 hour", instead of a count of secondsThe
errorsfunction now returns.is Retryable falsefor aConnectionwith reasonError networkand an HTTP 4xx status, such as a discovery 404, matching the SDK's own retries. A network failure without a status or with a 5xx status stays retryableThe default API
clientnow throws.observe State Configurationfor a listener that is not a function, instead of logging an observer failure on every state changeError The
Commandcomments no longer giveArgument Metadata user, which registration rejects, as a type example, and say that a custom argument can also userest -
e5e7d10: A session saved by
shardingis now resumed at most once. Before, a restart that resumed a saved session and then crashed left the old snapshot in the store, so a restart within 60 seconds of the original shutdown could resume it again and receive the same dispatches a second time, including any dispatch that caused the crash. Before resuming, startup now saves a copy of the loaded snapshot with an empty.sessions session. A later load of that copy starts a new session and logsId lifecycleat Warn, until the next shutdown saves a resumable session again. Custom stores receive this extra.session Snapshot Ignored savecall at startup. If it fails, the shard logslifecycleand starts a new session instead of resuming.session Save Failed Durations in log messages no longer read "1 min 60 s" or "60.0 s" near a minute boundary, or "1000 ms" just below a second. They now read "2 min 0 s", "1 min 0 s" and "1.0 s"
A command reply for a rejected
durationargument now shows its bounds in the argument's own syntax, such as "from 1s to 1h30m", instead of in milliseconds such as "from 1000 ms to 5400000 ms" -
3dc3081: An explicit
clientof a connected client now logs a failed cleanup step, such as a socket close that throws or an event handler or collector callback whose cancellation cleanup fails, at Error in the lifecycle category and counts it in.shutdown() diagnostics(). Its final record previously said the shutdown completed and the counter stayed at 0.counters .cleanup Failures -
3dc3081: When
clientcancels a running message or reaction collector callback whose cleanup fails, that failure now reaches the shutdown outcome, as an event handler's cleanup failure already did. It previously appeared only in the collector's own result.shutdown() -
a89dc6d: The package's documentation links, the agent guide and the
initcommand's guide link now point to the website athttps://.fluxerly .neontechspace .com -
963f0be: The comments on
GuildandBan .moderator Id Auditnow state that the IDLog Entry .user Id "0"is the Fluxer system user. Fluxer records a member removal, ban or ban removal made by its staff under that ID, and a staff ban stores it as the moderator. An invite pause that Fluxer places, lifts or lets expire is also recorded under"0"as aGuildentry. Both IDs already decoded, so no behavior changesUpdate -
e5e7d10: With the default
childofOutput prefix, a supervisor now reads each child record and prints it in the format of the stream it goes to. SDK records print as readable labeled lines on a pretty stream, and as JSON Lines with afieldsentry on a.child jsonstream. For this, default-API children always logjson, as do Effect API children whoseloggingis.format json. SettingFLUXERLY_orLOG_ FORMAT FLUXERLY_in the child environment still takes precedenceLOG_ COLOR Before, starting a bot with a terminal on standard output and standard error redirected, such as
node supervisor, gave children readable colored lines. Their Warn and Error records then reached the parent's JSON standard error as.js 2> errors .log supervisortext, without their level, code and fields, and without the documented.child Output fieldsentry. Those records now keep their shape and gain.child fields, while standard output still shows readable lines.child -
e831234: The supervisor no longer fails with a
Supervisorof reasonError spawnwhen it stops a child while the supervisor keeps running, such as for a reshard or an unconfirmed Identify permission, and a message to that child fails because the child is exiting on its own at that moment. The supervisor waits for that exit, or force-terminates the child at theshutdowndeadline, and then handles the exit as usual, in both APIsTimeout Ms -
1d54b09: The supervisor
waitmethod now returns success after a normal shutdown even when a child's stop request fails because the child is exiting on its own at that moment. It previously returned aFor Close Supervisorof reasonError spawn. The shutdown completes once the child exits, or at theshutdowndeadline for a child that is still running, in both APIsTimeout Ms -
99bf06d: The test kit now normalizes message content as Fluxer does. Fluxer removes form feed (U+000C) and U+202E from sent message text and trims surrounding whitespace before it stores and delivers the message, so a user who types
!pingreaches the bot as!ping. Before,saydelivered the content unchanged, so a test could ignore input that a live bot answers, or the other way round. Nowsaydelivers the normalized content, and the automatic reply to an unregistered message send or edit echoes the normalized content too. A test that expected a bot to ignore content with surrounding whitespace throughsaynow sees the bot answer it, as it would on Fluxer. Deliveringfixturesthrough.message emitstill passes content unchanged -
f1aebe9: The coding agent guide's list of APIs that Fluxerly does not have, such as
clientand message methods, now stands on its own without naming another library. Its rules section, which.login fluxerly agentscopies into an application'sAGENTS.md, keeps the instruction to use only declared methods. Run the command again to refresh an existing section -
6655b27: The
AGENTS.mdsection thatfluxerly agentswrites now recordsnpx --no fluxerly agentsorbunx --no-install fluxerly agentsas its refresh command, and pnpm projects keeppnpm exec fluxerly agents. These forms never install a package automatically, because the unscopedfluxerlyname on npm is not this SDK, so the SDK must already be installed in the project. Run the command again to update an existing section -
abb5d3f: API comments now explain recent Fluxer behavior. Member search can fail with
twoin a community that requires two-factor authentication for moderators, even without filters. The olderFactor Required nsfw: falsechannel field restores inheritance on create and edit, andnsfwmarks a channel as not adult-only. Message search in an age-restricted community can include adult content even whenOverride: false includeis false. Member queries over the gateway match the start of a nickname, global name or username. A rejected bot token or OAuth grant can come from the owning account's standing, which a new token does not fixNsfw -
7733b23: TypeScript now reports a mistake in one argument descriptor of
registeror the keyedMany commandsobject ofrun, such asBot { type: "int" }, on that descriptor with the list of valid types, and the other commands keep their inferredvalues. Previously the error named the whole schema and every command lost its value types -
1afd073: Reject malformed array entries using their indexed values while preserving documented local bounds
Reject impossible calendar dates in embed timestamps, pin cursors, guild history cutoffs and status expiry before dispatch or retention. Apply the same calendar validation to received message, pin, channel, guild, member, invite, ban and discovery timestamps
-
1afd073: Expose Fluxer's recorded positive member-ban message-deletion duration through audit-log REST reads and gateway events
-
1afd073: Honor inherited and non-enumerable audit-log iterator bounds, preserving requested cursors and enforcing page limits through both APIs
-
1afd073: Keep malformed nested allowed-mention selections and embed values other than the converted color and timestamp intact when building message inputs, so message-operation validation rejects them before a request starts
-
1afd073: Preserve inherited and non-enumerable supported structural properties when builders snapshot embeds, attachments, message references and allowed mentions. Builders retain unknown own enumerable fields for message-operation validation and attachment source references
-
f0e7941: The
Cacheexample now tracks cached keys, so replaced entries and cleared kinds stay accurate. TheChange Resourcedescription now names the one preload, the refill after a shard resumes a stored session, whichCache Settings shardingcontrols.refill Caches -
7733b23: Expire entries in every cache category through an expiry heap and reschedule the expiry timer only when the next deadline changes, so timed cache writes no longer scan every retained entry
-
7733b23: A caller AbortSignal whose
addthrows no longer leaves theEvent Listener runSIGINT and SIGTERM listeners installed. TheBot runfunction now registers the caller signal listener before its process signal listeners and removes it after themBot -
139f4dc: Include standalone JavaScript, TypeScript and native Effect bot starters using the public SDK runner for critical subscriptions and awaited cleanup
Clarify ordinary handler Promise ownership and add checked-JavaScript consumer coverage and symptom-based troubleshooting using existing diagnostics
-
1afd073: Accept voice-channel bitrate requests through 384,000 bits per second and return the value applied by Fluxer
-
5f6b6dd: Refresh the package README with matching JavaScript, TypeScript and Effect starter setup, explicit lifecycle ownership and installed Effect version guidance
-
d19d3c7: Prefix commands now run only for messages a person typed, meaning the
DefaultandReplymessage types, and skip every other type. When a thread is created, Fluxer posts a notice of type 18 into the parent channel, with the thread's name as its content and the thread's creator as its author, so a thread named!pingran the!pingcommand as its creator. The messagetypeis now part ofMessage, so every client keeps it, even with aCore messageselection that leaves it out. A message without aFields type, such as a hand-built test message, still counts as typed -
ce65b67: Reading the bot's community list page by page no longer repeats one page when Fluxer returns a page that does not move past the previous one. Automatic sharding now fails at once with a discovery
Connectionthat says so, and the supervisor's count forError totalreports that error as the cause of itsShards: "auto" shardfailure. The count previously requested the same full page again untilCount connectionpassed and then failed with a.startup Timeout Ms Connection. The cache refill after restored sessions now stops at such a page and logsTimeout Error lifecycleat Warn, where it previously requested the same page until the client closed without logging the refill.cache Refill -
ce65b67: Log deduplication now keeps records about different shards or subscriptions apart. The same malformed dispatch on two shards previously collapsed into one record within the dedupe window, so the second shard's fault appeared only as a repeat count with the first shard's ID
-
2b9efdf: Cancelling a connect while a failed discovery response is being cleaned up again keeps the discovery
Connectionalongside the interruption and the cleanup defect. The native Cause holds all three, and the default API'sError Sdklists them as reasons. With Effect 4.0.1, the discovery failure had been droppedDefect -
7733b23: The
displayhelper accepts a message author directly. Its user parameter now allows an absent.name display, as on received message authors, and falls back to the usernameName -
3c685e3: The deploying guide and the agent guide now say which
runhandles SIGINT and SIGTERM by default. The default API does unlessBot processisSignals false, while the native Effectrunhandles them only withBot process, because an Effect launcher such asSignals: true Nodealready interrupts the program on both signals and an interrupted program skips the drain. Runtime behavior is unchangedRuntime .run Main -
37612fd: The comment on the Effect API's
Botno longer calls a missing or blank token misuse that dies withOptions Configuration. AsError runalready behaved and documented, the bot fails with a typedBot Configurationonce the other options are validError -
7733b23: Start faster by importing each Effect module the SDK uses from its own
effect/<Module>subpath instead of theeffectpackage index, which loads every Effect module. Importing@neontechspace/now loads 87 Effect modules instead of 203, andfluxerly @neontechspace/loads 133fluxerly/ effect -
abb5d3f: Embed media can reference any image or video attachment through
attachment://, following Fluxer's file-type detection, instead of only PNG, JPEG, WebP and GIF files, in both entry points -
7733b23: Loading the package with
require()now fails with an error explaining that the SDK is ESM-only and how to switch the project toimport -
1afd073: Identify affected generic event subscriptions in safe fallback diagnostics
-
c3bbcf9: Cache kinds with a
maxexpiry now release a replaced or removed snapshot as soon as the cache drops it. Previously the expiry schedule kept such snapshots in memory until their original deadline came up or enough replacements accumulated to rebuild the schedule, so frequently updated entries of every cache kind, fromAge Ms messagestoemojisandstickers, could hold more memory than the cache limits suggested -
abb5d3f: A successful emoji or sticker metadata or source lookup no longer pauses unrelated requests such as message sends. Fluxer limits these lookups in one bucket per bot across all emojis or stickers, and the SDK now tracks them that way, so running out of that bucket still delays the next lookup of any emoji or sticker
-
7733b23: Pace presence, member-subscription, member-chunk, count and
gatewaycommands on each gateway connection to at most 500 in any 60-second window, sent in order, so they stay within Fluxer's budget of 600 payloads per connection instead of closing it with 4008. Heartbeats, Identify and Resume are never delayed behind them.send -
abb5d3f: Gateway member requests, presence member selection and member count requests now reject IDs above 9223372036854775807 before sending, in both entry points. Fluxer ignores such IDs, so a request could wait until its timeout, and an explicit member list made only of such IDs could turn into a request for all members
-
abb5d3f: When the SDK plans to resume a gateway session, it now closes the connection with code
4000instead of1000, in both entry points. Fluxer ends a session when the client closes with1000or 1001, so a reconnect after a missed heartbeat or a restart from aSessionhad to identify again and lost the events sent in between. A final shutdown without aStore Sessionstill closes withStore 1000 -
7733b23: Event handlers use much less CPU while a backlog waits for a free handler slot
-
3c685e3: The
fluxerly initcommand no longer refuses a folder that already has a.gitignore, such as the one the Create a bot guide has readers write before the quick start. It keeps that file and appendsnode_andmodules .envonly when a line is missing, leaving the rest and its line endings as they were, and a failed run still leaves the file untouched. Its next steps now link the guide that explains how to get the token and invite the bot to a community, and the quick start presentsinitbefore saving the token by hand. -
3c685e3: The TypeScript and Effect starters from
fluxerly initnow typecheck. Node.js runs a.tsfile by removing its types without checking them, so a type error in a generated project went unnoticed. Init now installs TypeScript 7, the version the SDK documents as required, together with the Node.js types, adds acheckscript that runs the compiler and prints the command to run it in its next steps, such asnpm run check,pnpm run checkorbun run check. The JavaScript starter is unchanged -
3a6b15b: Initial release of Fluxerly.js
-
06be6c0: The hosted
linksURL now starts at Fluxer's API authorization route,.installation https://, which redirects to the hosted installation page. The previousapi .fluxer .app/ v1/ oauth2/ authorize https://URL showed a page-not-found error. Installation links for a discovered instance still open that instance's web app directlyfluxer .app/ oauth2/ authorize -
9df8363: Update the WebSocket gateway transport dependency from ws 8.21.3 to 8.22.0
-
25125b0: Log records, unsafe payload records and
describeoutput now hide the user information of a URL, such asError user:passwordinhttps://, in both APIs. A user name without a password is hidden too, because it can be a credential on its own. The scheme, host and path stay readable, and anuser:password@host/ path @in a path, query or fragment is left as it was -
c927087: A
membersrequest that would be the client's 13th member request in 11 seconds now fails at once with.iterate Chunks MemberreasonChunk Error rateand aLimit retrywait, without being sent. Fluxer accepts 12 member requests per account in 10 seconds and drops the rest without an answer, so such a request previously waited out its whole timeout and failed with reasonAfter Ms timeout. The count covers one client, or the children of one supervisor together, and other processes with the same token are not counted. Member requests sent withgatewaycount toward the same window from the moment the socket takes them, but are never refused.send -
abb5d3f: With the users or direct-message cache enabled, a member update now removes that member's cached user and clears cached direct-message channels before handlers run, in both entry points. Fluxer reports username and other account changes to bots through member updates, so cached users and DM recipients could show the old identity. Automatic gateway event selection keeps member updates for these caches even without a
guildhandler. Later reads fetch fresh snapshots, and snapshots already returned stay unchangedMember Update -
7733b23: Keep a sent message in the message cache when its gateway echo arrives before the HTTP response. Fluxer omits the community ID,
guild_, from message HTTP responses, and the cache previously treated that difference as a conflict and dropped the message. Other differences between overlapping observations still remove the cached messageid -
7733b23: Read each message input field once when sending, replying, editing, forwarding, sending direct messages or executing webhooks, so the validated value is the one sent. A getter on
content,flags,embeds,sticker,Ids message, anReference allowedswitch or an attachment field can no longer pass validation with one value and send another. Other operations read caller arrays and options once too. TheMentions messages,.delete Many guilds,.fetch Counts channelsand.fetch Member Counts presencemethods copy their IDs once and use the validated copy. Cache entry limits, event wait options, presence input, pagination, search, member chunk, message cleanup, permission, instance resolution and attachment download options, webhook client options, supervisor options and client cache and upload settings read each field once.set Members -
1afd073: Remove unsupported message-search cursor inputs and result cursors. Traverse numbered pages with a fixed page size, report the provider's page ceiling explicitly and reject results with missing or unrelated channel context
-
1afd073: Reject array-valued OAuth operation options before discovery or request dispatch
-
7733b23: Keep the original OAuth response cleanup failure when reader cancellation or release fails, as REST and discovery cleanup already do. The native Cause holds it as the defect, the default API's
Sdkholds it as its cause, and several failures arrive as oneDefect Aggregate. Response bodies never become part of itError -
1afd073: Preserve unexpected OAuth construction failures as defects attributed to
oauth, with configuration details kept private.create -
1afd073: Keep OAuth consent, code exchange and revocation requests aligned with the values that passed local validation
-
ae161e3: A page read whose items arrive out of ID order now fails with
detailsset to.response Field order, as message history already did. This coversmembers,.fetch Page guilds,.fetch Page messagesand.fetch Reaction Users audit. Previously the error named the last field read from a valid item, such asLogs .fetch Page 1.deaf, which pointed at the wrong cause -
6563bf3: API comments now warn where Fluxer applies one operation as several separate writes. A failed
rolescan leave the role in place after some members already lost it, and a failed.delete channelscan leave a channel that already lost its invites, webhooks, attachments and messages. A failed overwrite change on a category, through.delete channels,.edit channelsor.set Permission Overwrite channels, can leave some of its channels with the old overwrites, and a retry may not reach them. The.remove Permission Overwrite channelscomment now says its error does not list the moves that finished, and the communities guide shows how to keep the failure while reading the actual order and parents. The announcement guide's publish helpers log the sent message's ID when publishing fails, and the guide notes that a failed publish can still leave the message published.reorder -
1afd073: Reject role and channel permission writes above Fluxer's signed 64-bit maximum while preserving unsigned 64-bit response values
-
1afd073: Prevent stale private-conversation snapshots from surviving overlapping direct-message mutations
-
ce65b67: A REST request that would wait behind a known rate-limit pause past its deadline now fails at once with reason
rateand the remaining wait inLimit retry, and logsAfter Ms ratelimit, at Warn for the first such request on a route each minute when the wait is a second or longer, and at Debug for every other such request. Previously it waited until its timeout and failed with reason.deadline timeout, so a long global 429 held every later request on the client for its whole deadline without saying why. The pause itself still lasts as long as Fluxer asked and ends on time, so requests sent after it go out normally -
1afd073: Accept static and animated custom-emoji markup in reaction requests and collectors. Parsed custom emoji, guild emoji snapshots and received reaction emoji now share the same input normalization across both APIs, including Unicode event values. Custom collectors continue matching by emoji ID after a rename
-
7733b23: Return the received failure at once when a GET retry after a network error or HTTP
500,502,503or504could not start before the operation deadline, such as a longRetry-After. Such a read previously waited for the whole deadline and failed with reasontimeout -
1afd073: Validate bounded resource input arrays from one indexed snapshot before encoding or dispatching requests
-
ce65b67: A connection lost while a REST response body arrives now fails with reason
network, keeping the sanitized transport code as the error's cause, and a read such asmessagesor a GET through.fetch restretries it at most twice like any other network error. Previously the operation failed with reason.request response, as if Fluxer had sent malformed data, logged a misleadingrestWarn and reported the raw transport error as an SDK defect, so the read was never retried. A write whose response is cut off still fails with outcome.response Rejected unknownand is never sent again -
1afd073: Preserve HTTP rejection status and cleanup defects when releasing an error-response reader fails, instead of reporting an ordinary network error
-
01caebf: Preserve inherited and non-enumerable command fields during batch registration in both APIs, so supplied guards, argument schemas, cooldowns and rejection callbacks cannot silently disappear
Snapshot recognized command and cooldown fields before validation and retain those same values, preserving atomic registration and preventing changing getters from replacing validated policies
-
4ce40a1: Release event handler slots and partition keys after a synchronous handler or middleware throw, including when middleware calls next. The failure is reported once and the invocation emits its failure observation without blocking later events
Keep guards returning false silent under automatic rejection feedback, including the
runreply default. Rejection counters, Debug records and customBot oncallbacks still receive the denial, while guards returning an explicit deny reason retain their replyReject Include the matched command's canonical name and reported failure in each command router observation, with one observation spanning event and command middleware. Interrupted commands report cancelled, and messages with no command match remain unnamed observations. Command failure metrics use the same name and command spans cover middleware, guards and execution
-
4ce40a1: Retain the gateway command pacing history across Resume within a running client, so reconnecting cannot restart a full send budget while Fluxer still counts the previous session's commands. A fresh Identify starts a new budget
Withdraw unsent community-count, channel-member-count and member-chunk requests when cancelled or timed out, preventing stale requests from being sent later. Bound internal waiting commands and report queue pressure through the existing busy failures
Release a cancelled, unsent
gatewaycommand's local queue capacity immediately, so replacement work can wait instead of failing as busy until the pacing window opens. Queued commands are still abandoned on disconnect rather than replayed.send Coalesce outgoing status changes to one unsent update per shard, keeping only the latest intent while the gateway send budget is full. Measure the four-second interval from actual transmission, and avoid retaining timers that already fired during scheduling
Replace queued member-presence selections with the latest IDs and withdraw an unsent selection when cleared. Start member-selection spacing at actual transmission, so pacing delays cannot flush obsolete selections or clears together
-
4ce40a1: Match a handler's failed HTTP
401or403request by error identity, including wrapped causes, rather than status and Fluxer code. Unknown codes no longer cause duplicate rejection logs, and a handled request keeps its Warn when a different request with the same code fails the handler. A rejection is logged once unless the handler fails more than one second later, after its Warn has already appearedKeep credential-key values masked in unsafe REST payload logging when a response exceeds the 65,536-byte read limit, including passwords and cookies in truncated JSON text. Invite-shaped objects in truncated lists also keep their access codes masked, while unrelated diagnostic and provider codes stay readable
Mask credential patterns in application error names and string codes in log records, failure descriptions,
describeand native Effect log causes and annotations. SDK error JSON also masks names in summarized causes, without changing the original error or its causeError List Debug for handled failure reports in the error and log code catalogue, alongside the existing unhandled failure levels
-
4ce40a1: Classify failures from OAuth identity, community and connection reads and token introspection as reads in their messages, hints and details, so
errorscan identify eligible transient failures. Code exchange, refresh and revoke calls with unknown outcomes remain unsafe to repeat, and OAuth requests are not retried automatically.is Retryable Preserve sanitized transport causes, including safe codes such as
ECONNRESET, when discovery failures become resource errors and during OAuth calls and buffered or streamed attachment downloads. Raw transport errors and their private text are not retainedRetain unknown uppercase Fluxer error codes in
detailsfor HTTP.provider Code 429failures, including when the required wait exceeds the operation deadlineReject raw REST paths whose reserved
/v1prefix or token-authenticated webhook route names use percent-encoded unreserved characters. Keep route checks case-sensitive and do not decode reserved charactersCompare pin page ordering and pagination progress at full fractional timestamp precision, with timezone offsets normalized by instant. Equal timestamps still remain equal
Reject audit message-deletion durations whose milliseconds fall outside the safe integer range, rather than projecting an overflowed value such as
Infinity, and round other durations to whole milliseconds. The same validation applies to REST audit pages and gateway audit events -
4ce40a1: Clear a shard's cached communities and dependent resources when partial replay of a saved session falls back to Identify. Clear reads cached during the outage again at
READY, while retaining replayed observations after a successful ResumeMake the in-memory test gateway enforce the session's Identify event filtering, including message mention exceptions and filtering retained across Resume. Emitting a dispatch Fluxer would suppress now fails with
Configurationinstead of making an unrealistic test passError Keep
MESSAGE_available under automatic filtering when aREACTION_ ADD messagehandler is registered, because Fluxer generates reaction batches only from unsuppressed additions. Test reaction batches use the same source filter, including when an explicit list ignores only the generated batch nameReaction Add Many -
4ce40a1: Effect supervised children let running handlers, queued handler events and REST requests finish within the parent's drain allowance before closing when the parent stops them, including a move to a larger automatic shard plan. Failure still stops running work without draining
Supervisor child-run options and default readiness-wait options reject unsupported keys before waiting for the parent or observing readiness. Misspelled keys include a suggested supported name, as do assignment and Identify options
Both
runentry points mask the normalized token in failure reports before a client exists, including tokens surrounded by whitespace and matching quotesBot Effect
runretains readable token, logging andBot reportsettings when an option getter throws. A throwing getter no longer discards a configured log sink, token masking orFailure reportFailure: false -
6eaecbf: Align request text validation with Fluxer's field-specific normalization and UTF-16 limits without rewriting accepted input
Preserve global rate-limit pauses when error bodies are unavailable and learn server rate-limit buckets with bounded, resource-aware tracking
Bound complete gateway messages before decoding, including fragmented frames, and reject invalid UTF-8 through the transport
Keep attachment reads responsive to cancellation and deadlines when sources repeatedly yield empty chunks
Use ordinary JavaScript and TypeScript extensions inside ESM package scopes while retaining the existing public package entry points
-
1afd073: Preserve native shutdown interruption and cleanup-defect reasons without exposing a connection failure through shutdown. Keep both classifications in the default API's
SdkreasonsDefect -
7733b23: Ship JavaScript source maps that point to the package's
src/files instead of embedding a second copy of every source file, which reduces the installed size. Stack traces with--enable-source-maps, debuggers and go-to-source still resolve to the original TypeScript -
7733b23: Large sharded bots now start within the default startup deadline. When a client owns several shards and has no
shardingcoordinator, the SDK spaces new-session Identify commands by one second, as before, and now adds one second to the startup deadline for each shard after the first, so this spacing alone never fails startup. An Info record with code.identify lifecyclestates the extended deadline.startup Deadline -
7733b23: Keep the original thrown value of an attachment stream cleanup failure that no caller observed. Client shutdown reports up to 64 retained failures with their values, and later ones are counted without their values
-
95ca9f2: The children of a supervisor now share the Fluxer limits that apply to the whole bot account, in both APIs. When one child is rate-limited globally, the parent holds the other children's REST requests until the same time, so they no longer keep sending into the pause and collect more
429responses. Member requests from all children share the member request limit through the parent. A child that gets no answer from its parent within one second counts only its own member requests and logssupervisorat Warn once. Per-route rate limits stay with each child. A client's first REST request also no longer waits out a global pause while it discovers the Fluxer instance, so it fails at once when the pause outlasts its deadline.member Requests Local -
7733b23: In the default API, a
configurecallback ofsupervisorthat throws or rejects makes.child .run childreject with.run SdkcodeDefect application. Its cause is an.defect Applicationwith sourceError supervisor child configure, whose own cause is the original value -
1afd073: Preserve inherited and non-enumerable client settings in both supervisor child APIs, so an explicitly selected instance is not silently replaced by hosted Fluxer. Reject inherited token and sharding overrides as well as own-property overrides
-
ce65b67: The supervisor now logs why before it acts. A child that misses
startuplogsTimeout Ms supervisorat Error before the supervisor shuts down. A child force-terminated after ignoring a stop request logs.startup Timeout supervisorat Warn, and one that lost its message channel and kept running logs it at Error before the supervisor shuts down. A child that does not confirm sending Identify within 5 seconds of its permission logs.terminated supervisorat Warn before it is stopped. A failed read of a child's output, which ends forwarding of that stream while the child keeps running, logs.identify Unacknowledged supervisorat Warn with the error. A failed message send to a child now keeps its error in the.output Failed supervisorrecord.spawn Failed -
0b7c49f: A supervisor with
totalnow completes its move to a larger plan when a child's shard asks to start a session, or reports one it just started, while every child stops for the move. Either message previously failed the whole supervisor with reasonShards: "auto" protocolinstead of moving. A request during the move stays unanswered until its stopping child withdraws it, and an Identify that a child sent after its permission still counts, so the larger plan's first Identify waitsidentifyafter it.minimum Spacing Ms -
01caebf: Treat runner-initiated client closure as normal when stopping a bot, while preserving run and cleanup failures
Preserve combined native failures and default-API
Sdkreasons when an operation and cleanup both failDefect -
37612fd: The test clients of both testing entry points now see every unhandled failure, whatever their logging settings. Previously
failures()and the shutdown check read failures from the captured log records, so a test client with a logging level aboveerroror a silenteventsorcommandscategory hid a broken handler, and shutdown passed. A failure repeated within the log deduplication window also appeared infailures()only once, and shutdown then rejected for the summary of the suppressed repeats. Each failure now appears infailures()once and counts for shutdown, whilelogs()still follows the logging settings -
0bca1c9: The
idle()andsaywaits of both testing entry points now also wait for received events whose handler has not started yet. Previously they counted only handlers that were running, so a wait could settle between two invocations while emitted events still waited in a handler's queue, and a test then asserted before its handler ran. This happened mostly with Effect API handlers, which run on the registering caller's scheduler -
1afd073: Capture message targets, search scopes and traversal bounds, pin and reaction-user cursors, and reaction emoji identities once before validation and dispatch, preventing later caller-controlled property reads from changing an accepted request
-
1afd073: Keep webhook create and edit requests aligned with the settings that passed local validation