Problem
Provider capabilities (character limits, media validation, comment support) are currently scattered across the codebase:
- Backend provider classes define
maxLength(), optional comment(), analytics(), and mediaCapabilities — but these are only accessible server-side.
- 30+ frontend provider components each hardcode
maximumCharacters and checkValidity in their withProvider() calls — duplicating what the backend already knows.
- No single source of truth — when a limit changes (e.g. X raising character limits for premium users), both backend and frontend must be updated independently.
This makes adding new providers tedious, updating limits error-prone, and the architecture harder to reason about.
Solution
Introduce a PostingCapabilities interface — a unified, server-computed descriptor that each provider generates once via introspection, and the frontend consumes from the API.
What PostingCapabilities captures
type PostingCapabilities = {
media: MediaCapabilities;
maxLength: number;
editor: 'none' | 'normal' | 'markdown' | 'html';
supportsComments: boolean;
supportsAnalytics: boolean;
supportsMentions: boolean;
commentsMediaSupport: boolean | 'no-media';
};
How it works
SocialAbstract.getPostingCapabilities() introspects the provider instance at runtime — checks for comment(), analytics(), mention() methods, reads mediaCapabilities, calls maxLength() with the integration's settings (resolving dynamic limits like X premium vs regular).
/integrations/list endpoint now includes postingCapabilities per integration, computed with the user's actual settings.
withProvider() HOC gains resolvedMaxChars, resolvedComments, and resolvedCheckValidity — each falls back to API-provided capabilities when the provider component doesn't explicitly override them.
validateMediaFromCapabilities() — a new frontend utility that performs generic media validation (image/video counts, mixing rules, required media) from the capabilities object, replacing 25 identical or trivial checkValidity implementations.
What changes across the codebase
Backend (6 files):
PostingCapabilities type + commentsMediaSupport added to SocialProvider interface
getPostingCapabilities() method on SocialAbstract
getSocialIntegration() return type updated to expose the method
/integrations/list controller resolves and includes postingCapabilities
- Instagram providers annotated with
commentsMediaSupport = 'no-media'
Frontend (33 files):
Integrations type extended with mediaCapabilities and postingCapabilities
withProvider() HOC updated with capability-aware fallbacks
- New
validateMediaFromCapabilities() utility
- 25 simple providers — removed both
maximumCharacters and checkValidity (now derived from API)
- 5 complex providers (X, Instagram, YouTube, TikTok, Pinterest) — removed
maximumCharacters only, retained checkValidity for platform-specific validation (video duration, cover images, trial reels, etc.)
Design decisions
- Introspection over declaration:
getPostingCapabilities() checks typeof self.comment === 'function' rather than requiring each provider to manually declare supportsComments: true. This means existing and future providers get correct capabilities automatically.
- Opt-in overrides: The HOC still accepts explicit
maximumCharacters and checkValidity — capabilities are a fallback, not a mandate. Complex providers keep their custom validation.
- Server-side resolution: Dynamic limits (X's 280 vs 4000 based on Verified setting) are resolved per-integration-instance on the server, so the frontend never needs to know the business logic.
Problem
Provider capabilities (character limits, media validation, comment support) are currently scattered across the codebase:
maxLength(), optionalcomment(),analytics(), andmediaCapabilities— but these are only accessible server-side.maximumCharactersandcheckValidityin theirwithProvider()calls — duplicating what the backend already knows.This makes adding new providers tedious, updating limits error-prone, and the architecture harder to reason about.
Solution
Introduce a
PostingCapabilitiesinterface — a unified, server-computed descriptor that each provider generates once via introspection, and the frontend consumes from the API.What
PostingCapabilitiescapturesHow it works
SocialAbstract.getPostingCapabilities()introspects the provider instance at runtime — checks forcomment(),analytics(),mention()methods, readsmediaCapabilities, callsmaxLength()with the integration's settings (resolving dynamic limits like X premium vs regular)./integrations/listendpoint now includespostingCapabilitiesper integration, computed with the user's actual settings.withProvider()HOC gainsresolvedMaxChars,resolvedComments, andresolvedCheckValidity— each falls back to API-provided capabilities when the provider component doesn't explicitly override them.validateMediaFromCapabilities()— a new frontend utility that performs generic media validation (image/video counts, mixing rules, required media) from the capabilities object, replacing 25 identical or trivialcheckValidityimplementations.What changes across the codebase
Backend (6 files):
PostingCapabilitiestype +commentsMediaSupportadded toSocialProviderinterfacegetPostingCapabilities()method onSocialAbstractgetSocialIntegration()return type updated to expose the method/integrations/listcontroller resolves and includespostingCapabilitiescommentsMediaSupport = 'no-media'Frontend (33 files):
Integrationstype extended withmediaCapabilitiesandpostingCapabilitieswithProvider()HOC updated with capability-aware fallbacksvalidateMediaFromCapabilities()utilitymaximumCharactersandcheckValidity(now derived from API)maximumCharactersonly, retainedcheckValidityfor platform-specific validation (video duration, cover images, trial reels, etc.)Design decisions
getPostingCapabilities()checkstypeof self.comment === 'function'rather than requiring each provider to manually declaresupportsComments: true. This means existing and future providers get correct capabilities automatically.maximumCharactersandcheckValidity— capabilities are a fallback, not a mandate. Complex providers keep their custom validation.