docs: add Fusor adapter authoring guide - #233
Conversation
📝 WalkthroughWalkthroughThe PR adds a comprehensive guide for building external Fusor-backed Spectrum adapters. It also updates contribution guidance to use the current provider contract, including ChangesFusor adapter authoring
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
@spectrum-ts/core
@spectrum-ts/elysia
@spectrum-ts/express
@spectrum-ts/fastify
@spectrum-ts/hono
@spectrum-ts/imessage
@spectrum-ts/imessage-local
@spectrum-ts/slack
spectrum-ts
@spectrum-ts/telegram
@spectrum-ts/terminal
@spectrum-ts/whatsapp-business
commit: |
There was a problem hiding this comment.
Problem vs fix
What problem is this PR trying to solve?
Two related documentation problems:
-
CONTRIBUTING.mdwas steering people at retired APIs. Before this change, the “add a platform provider” checklist still told contributors to implementspace.resolve,events.messages, andactions.send. Those names are gone from the current provider contract (user.resolve/space.create, top-levelmessages/send). That is not a style nit — it would send a new adapter author down a dead path. -
External Fusor adapter authors did not have a complete, current walkthrough. Fusor-backed providers have non-obvious rules: verify the exact
rawBody, keep platform IDs consistent across routing, typemessagesasFusorMessagesso overload selection works, understand at-least-once delivery, and treat inbound Fusor vs direct outbound API correctly. Telegram is the reference implementation, but reading that package alone is a high bar for someone publishing an independent adapter.
Is that problem valid?
Yes. The old CONTRIBUTING.md checklist is objectively wrong against today’s code. The Fusor authoring surface is also complex enough that “read Telegram and guess” is a real gap — especially for security-sensitive pieces like signature verification and replay windows.
Is the proposed fix the right solution, or only a patch over a deeper issue?
For what this PR claims to fix, this is the right kind of fix — not a symptom patch.
- Updating the contributor checklist is the direct cure for the stale API names. There is no deeper code change needed for that bug; the docs were simply left behind after the provider API refactor.
- Adding
FUSOR_ADAPTERS.mdis also the direct cure for the missing walkthrough. It matches the real Telegram wiring (definePlatform+fusor+ typedmessages+ top-levelsend), and it correctly calls out pitfalls that are easy to miss in the type system (especially theFusorMessagesoverload selection rule).
That said, there is a deeper process issue underneath the stale checklist: docs can drift when the provider contract changes. This PR repairs the immediate damage, but it does not fully close that loop:
- The published custom platforms guide already has a Fusor section, and its example still uses an untyped inline
messagesarrow — the exact footgun this new guide warns against. - The new guide lives at the repo root and is linked from
CONTRIBUTING.md, but it is not wired into the docs site nav, so consumers who only readdocs.photon.codesmay still miss the better guidance.
So: ship this — it solves a real, current docs failure. As a follow-up, sync (or replace) the Fusor example in docs/custom-platforms.mdx.vel and decide whether this walkthrough should also live on the published docs site, so we do not end up with two sources of truth that drift again.
Verdict
Approve on substance. Valid problem, correct documentation-shaped solution, low risk (Markdown only). Residual risk is future doc drift / incomplete discoverability, not that this PR is papering over a runtime bug.
Sent by Cursor Automation: PR analyze
| - [Telegram inbound mapping](./packages/telegram/src/inbound/messages.ts) | ||
| - [Telegram outbound dispatch](./packages/telegram/src/outbound/send.ts) | ||
| - [Telegram webhook registration](./packages/telegram/src/webhook.ts) | ||
| - [Fusor provider types](./packages/core/src/fusor/types.ts) |
There was a problem hiding this comment.
Small residual risk, not a blocker: this guide correctly warns about typing messages as FusorMessages, but the Fusor example in docs/custom-platforms.mdx.vel still shows an untyped inline messages arrow. Until that published page is synced (or clearly points here), some readers may still hit the overload footgun this section is trying to prevent.
| - `lifecycle.createClient`; `lifecycle.destroyClient` is optional | ||
| - top-level `messages` — either a long-lived async iterable or a Fusor payload | ||
| handler | ||
| - top-level `send` — the outbound content dispatcher |
There was a problem hiding this comment.
This is the important direct fix. The old checklist (space.resolve / events.messages / actions.send) was actively wrong against the current definePlatform contract, so correcting these names here is solving the root docs bug rather than working around it.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@FUSOR_ADAPTERS.md`:
- Around line 177-191: Update acmePayloadSchema so unknown type values are
accepted through a catch-all envelope branch, while the message.created and
endpoint.challenge branches continue validating their required fields. Ensure
makeVerify passes unknown events to handleMessages for an undefined mapping
result, but malformed payloads with known event types remain rejected.
🪄 Autofix
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: 93ce6f16-0298-45cc-bfb0-c67fdcafee98
📒 Files selected for processing (2)
CONTRIBUTING.mdFUSOR_ADAPTERS.md
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: Cursor Bugbot
- GitHub Check: Cursor Automation: PR analyze
🧰 Additional context used
🪛 LanguageTool
FUSOR_ADAPTERS.md
[style] ~55-~55: Consider removing “of” to be more concise
Context: ...atform ID The same ID must be used for all of the following: 1. the first argument to `d...
(ALL_OF_THE)
🔇 Additional comments (2)
CONTRIBUTING.md (1)
127-140: LGTM!FUSOR_ADAPTERS.md (1)
98-100: 🗄️ Data Integrity & IntegrationKeep the
spectrum-tspackage references.
spectrum-tsand@spectrum-ts/coreare both published at12.7.0. The metapackage exportsspectrum-tsandspectrum-ts/authoring, so the shown peer dependencies and imports resolve correctly.@spectrum-ts/coreis an alternative, not a required replacement.> Likely an incorrect or invalid review comment.
| export const acmePayloadSchema = z.discriminatedUnion("type", [ | ||
| z.object({ | ||
| type: z.literal("message.created"), | ||
| eventId: z.string().min(1), | ||
| messageId: z.string().min(1), | ||
| chatId: z.string().min(1), | ||
| senderId: z.string().min(1), | ||
| text: z.string().min(1), | ||
| sentAt: z.iso.datetime(), | ||
| }), | ||
| z.object({ | ||
| type: z.literal("endpoint.challenge"), | ||
| challenge: z.string(), | ||
| }), | ||
| ]); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Allow unknown event types to reach the mapper.
makeVerify parses this schema before handleMessages runs. The closed discriminated union rejects every unknown event type, so the mapper cannot return undefined as promised later in the guide. A harmless platform extension becomes a rejected Fusor delivery.
Add a catch-all envelope branch and validate known event shapes before mapping them. Keep malformed known events rejected.
🤖 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 `@FUSOR_ADAPTERS.md` around lines 177 - 191, Update acmePayloadSchema so
unknown type values are accepted through a catch-all envelope branch, while the
message.created and endpoint.challenge branches continue validating their
required fields. Ensure makeVerify passes unknown events to handleMessages for
an undefined mapping result, but malformed payloads with known event types
remain rejected.


Summary
definePlatform, verification, mapping, send, lifecycle, testing, and webhook-registration patternsCONTRIBUTING.mdto replace retired provider API names and link the new guideWhy
The existing contributor checklist referenced retired APIs such as
space.resolve,events.messages, andactions.send. External adapter authors also lacked a complete, current walkthrough of the Fusor provider contract and its security and delivery semantics.Impact
Documentation only. No runtime or public API behavior changes.
Validation
bun run checkgit diff --cached --checkNeed help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Note
Low Risk
Markdown-only changes; no runtime or public API behavior is modified.
Overview
Adds
FUSOR_ADAPTERS.md, a end-to-end guide for publishing external Fusor-backed Spectrum adapters (Telegram as the reference: Fusor inbound, direct platform API outbound). It documents the current provider surface—definePlatform,fusor+verify, typedFusorMessages, top-levelsend,space.create, lifecycle/webhook registration—and covers security (signrawBody, replay windows), delivery semantics (app.messagesvsapp.webhook()), testing, and common pitfalls including retired APIs.CONTRIBUTING.mdis updated to link that guide for webhook-driven external packages and to replace the outdated minimum contract (space.resolve,events.messages,actions.send) withuser.resolve,space.create, top-levelmessages/send, and optionallifecycle.destroyClient.Reviewed by Cursor Bugbot for commit 56594d7. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit