Skip to content

feat: convert standard Markdown to Slack mrkdwn and WhatsApp formatting on fallback - #137

Open
feranmi (optimusbuilder) wants to merge 6 commits into
photon-hq:mainfrom
optimusbuilder:feat/markdown-converter
Open

feat: convert standard Markdown to Slack mrkdwn and WhatsApp formatting on fallback#137
feranmi (optimusbuilder) wants to merge 6 commits into
photon-hq:mainfrom
optimusbuilder:feat/markdown-converter

Conversation

@optimusbuilder

@optimusbuilder feranmi (optimusbuilder) commented Jun 18, 2026

Copy link
Copy Markdown

This PR implements automatic conversion of standard Markdown (CommonMark + GFM) into platform-specific rich text styles (Slack mrkdwn, WhatsApp formatting, and plain text fallbacks) inside the core send fallbacks pipeline.

Key Changes

  • Slack & WhatsApp Renderers: Added markdownToSlack and markdownToWhatsapp under packages/core/src/utils/markdown.ts using marked token parsing.
  • Pipeline Integration: Modified the sendWithFallbacks pipeline (replaceMarkdown / downgradeMarkdown) to receive the platform name and apply these translations.
  • Unit and Integration Tests: Added comprehensive test coverage validating elements (bold/italic/strike/links/escaping) and routing.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Summary by CodeRabbit

  • New Features

    • Added Fastify adapter support with a Spectrum webhook plugin available via a dedicated fastify entrypoint.
    • Exposed markdownToSlack and markdownToWhatsapp for richer platform-specific formatting.
  • Improvements

    • Improved markdown fallback when a provider doesn’t support markdown, using Slack/WhatsApp formatting across top-level and nested reply/edit/group content.
  • Bug Fixes

    • Normalized Express webhook headers passed to the app to ensure consistent webhook handling.
  • Developer Experience

    • Refined public entrypoint exports and added/updated coverage for Fastify/webhook and markdown conversions.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2dfc50cb-6f7f-4c54-b47b-ed44e8c2aefc

📥 Commits

Reviewing files that changed from the base of the PR and between 45cd3b9 and d1e3ad0.

📒 Files selected for processing (3)
  • packages/core/src/express.ts
  • packages/core/src/fastify.ts
  • packages/core/test/webhook/fastify.test.ts
 __________________________________________________________________________________________________________________________________________________________________________________________________________________
< In software, we rarely have meaningful requirements. Even if we do, the only measure of success that matters is whether our solution solves the customer's shifting idea of what their problem is. - Jeff Atwood >
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
📝 Walkthrough

Walkthrough

Adds markdownToSlack and markdownToWhatsapp converters to the markdown utility, integrates them into the platform build pipeline's fallback path so each platform receives correctly formatted text on UnsupportedError, and introduces a new Fastify webhook plugin (spectrum) with raw-body buffering, full package exports, build config wiring, and integration tests.

Changes

Platform-specific markdown converters and fallback routing

Layer / File(s) Summary
Slack and WhatsApp markdown converter implementations
packages/core/src/utils/markdown.ts, packages/core/src/authoring.ts
markdownToSlack and markdownToWhatsapp are added with full block/inline token rendering (emphasis, code, links, tables, lists, blockquotes). Both are re-exported from authoring.ts alongside renderInlineTokens.
Platform-aware fallback routing in build pipeline
packages/core/src/platform/build.ts
downgradeMarkdown(md, platform) replaces the single-converter downgrade, dispatching to Slack, WhatsApp, or plain-text converters by target platform. replaceMarkdown and the group path accept platform, and sendWithFallbacks passes it through.
Converter unit tests and fallback integration tests
packages/core/test/utils/markdown.test.ts, packages/core/test/core/send-markdown-fallback.test.ts
Adds describe blocks for markdownToSlack and markdownToWhatsapp covering emphasis, escaping, links, headings, and lists. Two new integration cases assert correct Slack/WhatsApp fallback output when the provider does not support markdown.

Fastify webhook plugin

Layer / File(s) Summary
Fastify plugin implementation and package/build wiring
packages/core/src/fastify.ts, packages/core/package.json, packages/core/tsdown.config.ts, packages/spectrum-ts/src/fastify.ts, packages/spectrum-ts/src/express.ts, packages/spectrum-ts/package.json, packages/spectrum-ts/tsdown.config.ts
Defines SpectrumPluginOptions and the spectrum Fastify plugin: clears default content-type parsers, installs wildcard raw-body buffer parser, mounts POST route forwarding to app.webhook and responding with status/headers/binary body. Re-exports WebhookHandler, Message, and Space. Wired into package exports, peerDependencies (with optional flag), tsdown entry/external for both core and spectrum-ts. Updates express.ts to explicit named exports, establishing consistent adapter pattern.
Fastify plugin integration tests
packages/core/test/webhook/fastify.test.ts
Three Bun test cases: valid signed webhook delivers message and returns 200; invalid signature returns 401 and skips onMessage; custom path option routes to the configured endpoint and returns 200.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant FastifyServer
  participant SpectrumPlugin
  participant WebhookReceiver
  participant onMessage

  Client->>FastifyServer: POST /spectrum/webhook (raw bytes)
  FastifyServer->>SpectrumPlugin: wildcard parser buffers body into Buffer
  SpectrumPlugin->>WebhookReceiver: app.webhook(request, onMessage)
  WebhookReceiver->>onMessage: verified Message delivered
  onMessage-->>WebhookReceiver: handler resolves
  WebhookReceiver-->>SpectrumPlugin: {status, headers, body}
  SpectrumPlugin-->>Client: HTTP response with status + binary body
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • photon-hq/spectrum-ts#126: Introduced the app.webhook(request, handler) interface and Hono/Express/Elysia webhook adapter pattern that the new Fastify plugin directly follows.

Suggested reviewers

  • underthestars-zhy

🐇 A new path for Fastify hops into view,
With Slack stars and WhatsApp bold text too!
Raw bytes buffered, signatures checked tight,
Markdown falls back with platform-perfect light.
The rabbit types fast — *Hi* sent just right! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: implementing Markdown-to-platform-specific formatting conversion (Slack mrkdwn and WhatsApp) for the fallback pipeline.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot added the release Just as it is label Jun 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/platform/build.ts (1)

389-391: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make the fallback log message platform-agnostic.

Line 390 currently says fallback sends “plain text”, but Slack/WhatsApp paths now send platform-formatted downgraded text. This can mislead debugging and telemetry interpretation.

Suggested patch
-      `${platform} does not support markdown; sending the content as plain text instead.`,
+      `${platform} does not support markdown; sending downgraded text content instead.`,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/platform/build.ts` around lines 389 - 391, The log message
in the platformLog.info call states content is being sent as "plain text" when
markdown is not supported, but different platforms like Slack and WhatsApp now
send platform-formatted downgraded text instead of actual plain text. Update the
message at line 390 to be platform-agnostic by removing the specific reference
to "plain text" and instead use a more generic description that accurately
reflects that the content is being sent in an alternative format without
claiming it is plain text.
🧹 Nitpick comments (1)
packages/spectrum-ts/src/fastify.ts (1)

4-4: ⚡ Quick win

Replace wildcard re-export with explicit exports to keep the surface controlled.

export * violates the repository rule against barrel-style re-exports and can unintentionally widen the metapackage API over time.

♻️ Proposed change
-export * from "`@spectrum-ts/core/fastify`";
+export { spectrum } from "`@spectrum-ts/core/fastify`";
+export type {
+  SpectrumPluginOptions,
+  WebhookHandler,
+  Message,
+  Space,
+} from "`@spectrum-ts/core/fastify`";

As per coding guidelines, "Avoid barrel files (index files that re-export everything) in JavaScript/TypeScript".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/fastify.ts` at line 4, The wildcard re-export
statement export * from "`@spectrum-ts/core/fastify`" violates the repository
guidelines against barrel-style re-exports. Replace this wildcard export with
explicit named exports by identifying which specific types, functions, or
interfaces are being exported from the "`@spectrum-ts/core/fastify`" module and
list them individually in the export statement instead of using the wildcard.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/test/webhook/fastify.test.ts`:
- Line 1: Save the original value of process.env.SPECTRUM_WEBHOOK_SECRET before
the test suite runs and restore it after the suite completes. Use a beforeAll
hook to store the original value and an afterAll hook to restore it, ensuring
that any modifications to process.env.SPECTRUM_WEBHOOK_SECRET during the test
suite (around lines 21-23 where it is mutated) do not leak into other test
suites. This pattern prevents nondeterministic behavior in test execution order.

---

Outside diff comments:
In `@packages/core/src/platform/build.ts`:
- Around line 389-391: The log message in the platformLog.info call states
content is being sent as "plain text" when markdown is not supported, but
different platforms like Slack and WhatsApp now send platform-formatted
downgraded text instead of actual plain text. Update the message at line 390 to
be platform-agnostic by removing the specific reference to "plain text" and
instead use a more generic description that accurately reflects that the content
is being sent in an alternative format without claiming it is plain text.

---

Nitpick comments:
In `@packages/spectrum-ts/src/fastify.ts`:
- Line 4: The wildcard re-export statement export * from
"`@spectrum-ts/core/fastify`" violates the repository guidelines against
barrel-style re-exports. Replace this wildcard export with explicit named
exports by identifying which specific types, functions, or interfaces are being
exported from the "`@spectrum-ts/core/fastify`" module and list them individually
in the export statement instead of using the wildcard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a1b0f91b-4a03-4351-9024-adb056d4baf3

📥 Commits

Reviewing files that changed from the base of the PR and between ed88e09 and d4a2c64.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • packages/core/package.json
  • packages/core/src/authoring.ts
  • packages/core/src/fastify.ts
  • packages/core/src/platform/build.ts
  • packages/core/src/utils/markdown.ts
  • packages/core/test/core/send-markdown-fallback.test.ts
  • packages/core/test/utils/markdown.test.ts
  • packages/core/test/webhook/fastify.test.ts
  • packages/core/tsdown.config.ts
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/fastify.ts
  • packages/spectrum-ts/tsdown.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/tsdown.config.ts
  • packages/core/src/authoring.ts
  • packages/core/src/fastify.ts
  • packages/spectrum-ts/src/fastify.ts
  • packages/core/test/webhook/fastify.test.ts
  • packages/core/tsdown.config.ts
  • packages/core/test/utils/markdown.test.ts
  • packages/core/src/platform/build.ts
  • packages/core/src/utils/markdown.ts
  • packages/core/test/core/send-markdown-fallback.test.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/tsdown.config.ts
  • packages/core/src/authoring.ts
  • packages/core/src/fastify.ts
  • packages/spectrum-ts/src/fastify.ts
  • packages/core/test/webhook/fastify.test.ts
  • packages/core/tsdown.config.ts
  • packages/core/test/utils/markdown.test.ts
  • packages/core/src/platform/build.ts
  • packages/core/src/utils/markdown.ts
  • packages/core/test/core/send-markdown-fallback.test.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks in test files
Avoid done callbacks in async tests - use async/await instead in test files
Don't use .only or .skip in committed code in test files
Keep test suites reasonably flat - avoid excessive describe nesting in test files

Files:

  • packages/core/test/webhook/fastify.test.ts
  • packages/core/test/utils/markdown.test.ts
  • packages/core/test/core/send-markdown-fallback.test.ts
🔇 Additional comments (10)
packages/core/src/fastify.ts (1)

73-113: LGTM!

Also applies to: 115-117

packages/core/package.json (1)

47-50: LGTM!

Also applies to: 77-78, 108-108, 118-118, 132-134

packages/core/tsdown.config.ts (1)

10-10: LGTM!

Also applies to: 17-17

packages/spectrum-ts/package.json (1)

47-51: LGTM!

Also applies to: 87-90

packages/spectrum-ts/tsdown.config.ts (1)

10-10: LGTM!

packages/core/src/utils/markdown.ts (1)

168-459: LGTM!

packages/core/src/authoring.ts (1)

38-42: LGTM!

packages/core/src/platform/build.ts (1)

26-30: LGTM!

Also applies to: 250-299, 385-388, 392-397

packages/core/test/utils/markdown.test.ts (1)

2-6: LGTM!

Also applies to: 101-162

packages/core/test/core/send-markdown-fallback.test.ts (1)

300-348: LGTM!

Comment thread packages/core/test/webhook/fastify.test.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Just as it is

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant