Skip to content

Better type gaurds on channels and refactored Message class for future testing#20

Merged
blstmo merged 5 commits intofluxerjs:mainfrom
MusicMakerOwO:main
Feb 22, 2026
Merged

Better type gaurds on channels and refactored Message class for future testing#20
blstmo merged 5 commits intofluxerjs:mainfrom
MusicMakerOwO:main

Conversation

@MusicMakerOwO
Copy link
Contributor

@MusicMakerOwO MusicMakerOwO commented Feb 20, 2026

Description

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Checklist

  • My code follows the project's style guidelines (run pnpm run lint)
  • I have run pnpm run build successfully
  • I have run pnpm run test successfully

BREAKING CHANGE: Renamed Channel.isSendable() to isTextBased() for more clarity

Summary by CodeRabbit

Release Notes

  • Refactor

    • Updated Client.sendToChannel() parameter naming for clarity.
    • Renamed Channel.isSendable() to Channel.isTextBased() for consistency.
    • Enhanced type predicates for isDM(), isVoice(), and new isLink() channel detection methods, improving TypeScript type narrowing.
    • Improved Message.send() method signature for flexibility.
  • Bug Fixes

    • Fixed Webhook.send() to correctly use preprocessed options.

@vercel
Copy link

vercel bot commented Feb 20, 2026

@MusicMakerOwO is attempting to deploy a commit to the blstmo's projects Team on Vercel.

A member of the Team first needs to authorize it.

@blstmo
Copy link
Contributor

blstmo commented Feb 21, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

📝 Walkthrough

Walkthrough

This refactoring standardizes message sending across Client, Message, and Webhook classes by extracting payload creation into reusable helpers, updates Channel type guards to use type predicates instead of booleans, and constrains MessageSendOptions to object-only input rather than accepting direct strings.

Changes

Cohort / File(s) Summary
Message sending standardization
packages/fluxer-core/src/client/Client.ts, packages/fluxer-core/src/structures/Message.ts, packages/fluxer-core/src/structures/Webhook.ts
Refactored message sending flow: Client.sendToChannel() parameter renamed from payload to content; new Message._createMessageBody() static helper extracts payload creation logic; new Message._send() helper encapsulates posting; reply() refactored to use new helpers; Webhook.send() updated to use pre-processed options object for body building.
Channel type predicates
packages/fluxer-core/src/structures/Channel.ts
Improved type safety: isSendable() renamed to isTextBased() with type predicate return; isDM() and isVoice() changed to return type predicates; added new isLink() type guard; LinkChannel.url changed from optional to required (string | null).
Message options type constraints
packages/fluxer-core/src/util/messageUtils.ts
MessageSendOptions type signature updated to accept only object form, removing direct string support; callers must now provide an object with optional content, embeds, files, and attachments properties.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Hopping through the payloads bright,
Type guards now stand guard with might,
Helper methods extract with care,
Messages dance through the air!
From strings to objects, clean and tight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title mentions 'better type guards on channels' and 'refactored Message class' which directly align with the main changes (type guards added to Channel methods, Message class refactored for testability). However, it contains a typo: 'gaurds' should be 'guards'. The title is sufficiently clear and specific about the primary changes despite the spelling error.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/fluxer-core/src/structures/Message.ts`:
- Around line 180-196: The _createMessageBody function currently only blocks
empty string inputs; update it to validate object payloads after calling
buildSendBody so truly empty bodies (e.g. {}, { content: '' }, { embeds: [] })
throw the same RangeError: after const base = buildSendBody(content), check that
base has at least one meaningful field (e.g. non-empty base.content or
base.embeds?.length or base.components?.length or other content-bearing keys)
and throw RangeError('Cannot send an empty message') if none are present; also
change the returned object construction to omit files when undefined using the
same conditional pattern as other callers (use files?.length ? { files, body } :
{ body } for both referenced_message and non-referenced branches). Ensure
changes reference _createMessageBody, buildSendBody, resolveMessageFiles, and
MessagePayload.

Comment on lines 285 to 290
async sendToChannel(
channelId: string,
payload: string | { content?: string; embeds?: APIEmbed[] },
content: string | { content?: string; embeds?: APIEmbed[] },
): Promise<Message> {
const payload = await Message._createMessageBody(content);
return this.channels.send(channelId, payload);
Copy link

@coderabbitai coderabbitai bot Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "class ChannelManager" packages/fluxer-core/src/client/ChannelManager.ts
rg -n "send\s*\(" -C3 packages/fluxer-core/src/client/ChannelManager.ts
rg -n "MessagePayload|SendBodyResult|files" packages/fluxer-core/src/client/ChannelManager.ts

Repository: fluxerjs/core

Length of output: 1902


🏁 Script executed:

rg -n "class Message" packages/fluxer-core/src
rg -n "_createMessageBody" -A5 packages/fluxer-core/src/client/Message.ts
rg -n "type MessagePayload|interface MessagePayload|type MessageSendOptions|interface MessageSendOptions" packages/fluxer-core/src

Repository: fluxerjs/core

Length of output: 913


🏁 Script executed:

cat -n packages/fluxer-core/src/structures/Message.ts | head -80
rg -n "_createMessageBody" -A10 packages/fluxer-core/src/structures/Message.ts

Repository: fluxerjs/core

Length of output: 4662


🏁 Script executed:

sed -n '181,210p' packages/fluxer-core/src/structures/Message.ts
rg -n "type MessageSendOptions|interface MessageSendOptions" -A10 packages/fluxer-core/src/util/messageUtils.ts

Repository: fluxerjs/core

Length of output: 1668


Fix type mismatch: sendToChannel passes MessagePayload but ChannelManager.send expects MessageSendOptions

Line 289 passes a MessagePayload ({ files, body }) to channels.send(), but ChannelManager.send() expects MessageSendOptions ({ content?, embeds?, files?, attachments? }). These are incompatible types. ChannelManager.send() calls buildSendBody(payload) and accesses opts.files, neither of which work correctly with the MessagePayload structure being passed in. Either:

  • Have sendToChannel() extract and rebuild MessageSendOptions before calling channels.send(), or
  • Add an overload to ChannelManager.send() that accepts MessagePayload and calls _send() directly instead of re-processing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix tomorrow morning, build did not report this 💔

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MusicMakerOwO Thanks for confirming! No worries—type mismatches like this can slip through if TypeScript's strict mode isn't fully enabled or if type assertions are masking the incompatibility. Looking forward to the fix tomorrow. Let me know if you'd like me to review the updated code once it's ready! 👍

Comment on lines +180 to +196
/** Exposed for testing purposes, use Message.reply() or send() for normal use */
static async _createMessageBody(
content: string | MessageSendOptions,
referenced_message?: { channel_id: string; message_id: string; guild_id?: string },
): Promise<MessagePayload> {
if (typeof content === 'string') {
if (content.length === 0) {
throw new RangeError('Cannot send an empty message');
}
content = { content };
}
const base = buildSendBody(content);
const files = content.files?.length ? await resolveMessageFiles(content.files) : undefined;
return referenced_message
? { files, body: { ...base, referenced_message } }
: { files, body: { ...base } };
}
Copy link

@coderabbitai coderabbitai bot Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for files property handling in REST client
rg -n "files" -g '*.ts' packages --max-count=50 | head -100

Repository: fluxerjs/core

Length of output: 6909


🏁 Script executed:

#!/bin/bash
# Look for multipart/form-data handling
rg -n "multipart|form-data" -g '*.ts' packages

Repository: fluxerjs/core

Length of output: 450


🏁 Script executed:

#!/bin/bash
# Search for the REST client request handling
fd -e ts packages -path '*/rest*' -o -path '*/request*' | head -20

Repository: fluxerjs/core

Length of output: 225


🏁 Script executed:

#!/bin/bash
# Check MessagePayload type definition
rg -n "type MessagePayload|interface MessagePayload" -g '*.ts' packages

Repository: fluxerjs/core

Length of output: 348


🏁 Script executed:

#!/bin/bash
# Look for buildSendBody and resolveMessageFiles functions
rg -n "buildSendBody|resolveMessageFiles" -g '*.ts' packages

Repository: fluxerjs/core

Length of output: 2906


🏁 Script executed:

cat -n packages/fluxer-core/src/structures/Message.ts | sed -n '39,50p'

Repository: fluxerjs/core

Length of output: 509


🏁 Script executed:

cat -n packages/fluxer-core/src/util/messageUtils.ts | sed -n '92,115p'

Repository: fluxerjs/core

Length of output: 1092


🏁 Script executed:

cat -n packages/rest/src/RequestManager.ts | sed -n '85,100p'

Repository: fluxerjs/core

Length of output: 639


🏁 Script executed:

cat -n packages/fluxer-core/src/util/messageUtils.test.ts

Repository: fluxerjs/core

Length of output: 2585


🏁 Script executed:

rg -n "Cannot send an empty|empty message" -g '*.ts' packages

Repository: fluxerjs/core

Length of output: 169


🏁 Script executed:

cat -n packages/rest/src/RequestManager.ts | sed -n '1,25p'

Repository: fluxerjs/core

Length of output: 883


Block empty object payloads and omit undefined files

Line 185 only guards empty string inputs. {}, { content: '' }, or { embeds: [] } still pass through buildSendBody without validation and can send an empty message to the API. Additionally, this method always includes files in the response even when undefined, inconsistent with how other callers (Channel, Webhook, ChannelManager) conditionally omit it using the pattern files?.length ? { body, files } : { body }.

🛠️ Suggested fix
   static async _createMessageBody(
     content: string | MessageSendOptions,
     referenced_message?: { channel_id: string; message_id: string; guild_id?: string },
   ): Promise<MessagePayload> {
     if (typeof content === 'string') {
       if (content.length === 0) {
         throw new RangeError('Cannot send an empty message');
       }
       content = { content };
     }
+    const hasContent = !!content.content?.length;
+    const hasEmbeds = !!content.embeds?.length;
+    const hasFiles = !!content.files?.length;
+    if (!hasContent && !hasEmbeds && !hasFiles) {
+      throw new RangeError('Cannot send an empty message');
+    }
     const base = buildSendBody(content);
     const files = content.files?.length ? await resolveMessageFiles(content.files) : undefined;
-    return referenced_message
-      ? { files, body: { ...base, referenced_message } }
-      : { files, body: { ...base } };
+    const body = referenced_message ? { ...base, referenced_message } : { ...base };
+    return files ? { files, body } : { body };
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluxer-core/src/structures/Message.ts` around lines 180 - 196, The
_createMessageBody function currently only blocks empty string inputs; update it
to validate object payloads after calling buildSendBody so truly empty bodies
(e.g. {}, { content: '' }, { embeds: [] }) throw the same RangeError: after
const base = buildSendBody(content), check that base has at least one meaningful
field (e.g. non-empty base.content or base.embeds?.length or
base.components?.length or other content-bearing keys) and throw
RangeError('Cannot send an empty message') if none are present; also change the
returned object construction to omit files when undefined using the same
conditional pattern as other callers (use files?.length ? { files, body } : {
body } for both referenced_message and non-referenced branches). Ensure changes
reference _createMessageBody, buildSendBody, resolveMessageFiles, and
MessagePayload.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completely skipped my mind but the suggested fix seems fine to just merge in

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MusicMakerOwO, thank you for confirming! I'll apply the suggested fix to add proper empty message validation and consistent file handling.

Task: Apply empty message validation fix to _createMessageBody

  • Add validation for empty object payloads after buildSendBody call
  • Update return statement to conditionally omit undefined files
  • Ensure consistency with other callers (Channel, Webhook, ChannelManager)

💡 For code changes across multiple files, use @coderabbitai plan in an issue comment

@blstmo blstmo merged commit 46c2922 into fluxerjs:main Feb 22, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants