@nan0web/share.app
v1.1.0
Published
Sovereign Social Distribution Layer (Rules Engine)
Maintainers
Readme
@nan0web/share.app
Sovereign Social Distribution Layer — Rules Engine for automatic content distribution across social platforms.
Configure your rules once. Then share.app publishes new content to the right platforms with the right delay — automatically.
Installation
npm install @nan0web/share.appQuick Start
import { DummyAdapter, evaluateRules, executeTasks } from '@nan0web/share.app'
const adapters = new Map([['dummy', new DummyAdapter({ account: 'me' })]])
const rules = [
{ name: 'All public posts', if: { tags: ['public'] }, publish: [{ adapter: 'dummy', delay: 0 }] },
]
const content = { text: 'Hello Sovereign World!', tags: ['public'], type: 'post', lang: 'uk' }
const tasks = evaluateRules(content, rules, adapters)
const results = await executeTasks(tasks)
// → [{ id: 'dummy-post-...', url: 'https://dummy.nan0web.app/posts/...' }]Architecture
Content (post / article / announcement)
│
▼
Rules Engine (evaluateRules)
│ matches conditions (tags, type, lang, hasMedia)
│ parses delay (0, '30m', '2h', '1d 09:00', 'Mon 10:00')
▼
Tasks [ { adapter, content, delayMs } ]
│
▼
executeTasks → adapter.verify() gate
→ immediate tasks run now
→ delayed tasks via setTimeout (dev) / Queue (prod)
│
▼
SocialAdapter.publish(content) → { id, url }
SocialAdapter.update(id, content) → { id, url }
SocialAdapter.delete(id) → trueCore Modules
SocialAdapter — Base Protocol
import { SocialAdapter, createLimits } from '@nan0web/share.app'
class MyAdapter extends SocialAdapter {
get id() { return 'my-platform' }
get capabilities() { return ['media', 'delete', 'reply', 'edit'] }
get limits() { return createLimits({ maxLength: 500 }) }
async verify() { return true }
async publish(content) { return { id, url } }
async update(postId, content) { return { id, url } }
async delete(postId) { return true }
async syncFeedback(postId) { return [feedback] }
async reply(target, text) { return { id } }
}RulesEngine — Delay Parser
import { parseDelay } from '@nan0web/share.app'
parseDelay('30m') // → 1_800_000ms
parseDelay('2h') // → 7_200_000ms
parseDelay('1d 09:00') // → ms until tomorrow 09:00
parseDelay('Mon 10:00') // → ms until next Monday 10:00Models — Self-Describing Schemas
import { SocialAdapterConfig, TelegramAdapterConfig } from '@nan0web/share.app'
const config = new TelegramAdapterConfig({ botToken: 'abc', chatId: '@ch' })
config instanceof SocialAdapterConfig // true
config.toJSON() // { botToken, chatId, parseMode, disableNotification, ... }
// Auto-documentation via static field descriptors:
TelegramAdapterConfig.botToken.help // 'Telegram Bot API token from @BotFather.'
TelegramAdapterConfig.parseMode.default // 'HTML'Model.describe() — Auto-Documentation (v1.1.0)
import { TelegramAdapterConfig } from '@nan0web/share.app'
const docs = TelegramAdapterConfig.describe()
// → [
// { field: 'botToken', help: 'Telegram Bot API token...', default: '' },
// { field: 'chatId', help: 'Target chat ID...', default: '' },
// { field: 'parseMode', help: '...', default: 'HTML' },
// ...
// ]Content Validation (v1.1.0)
import { SocialAdapterContent } from '@nan0web/share.app'
const result = SocialAdapterContent.validate({ text: 'Hello!', type: 'post' })
// → { valid: true, errors: [] }
const invalid = SocialAdapterContent.validate({})
// → { valid: false, errors: ['Content must have text or media'] }Full Lifecycle: Publish → Update → Delete
import { DummyAdapter } from '@nan0web/share.app'
const adapter = new DummyAdapter({ account: 'me' })
await adapter.verify()
// 1. Publish
const post = await adapter.publish({ text: 'First version', type: 'post' })
// → { id: 'dummy-post-...', url: 'https://dummy.nan0web.app/posts/...' }
// 2. Update (v1.1.0)
const updated = await adapter.update(post.id, { text: 'Updated version' })
// → { id: 'dummy-post-...', url: '...' }
// 3. Delete
const deleted = await adapter.delete(post.id)
// → trueAvailable Adapters
| Adapter | Platform | Status |
| ----------------- | -------------------------- | --------- |
| DummyAdapter | In-memory (test/reference) | ✅ v1.0.0 |
| TelegramAdapter | Telegram Bot API | ✅ v1.0.0 |
Planned: @nan0web/share-rss (RSS Feed), @nan0web/share-x (X/Twitter API).
Capabilities Token Reference
| Token | Meaning |
| ---------------------------------------- | ------------------------------------- |
| media | Platform accepts photo/video/document |
| edit | Platform supports editing posts |
| delete | Platform allows deleting posts |
| reply | Platform supports native replies |
| threads | Platform supports threaded posts |
| photo / video / document / audio | Specific media types |
adapter.can('media') // true / false
adapter.can('edit') // true / false
adapter.can('delete') // true / falseTests
npm test # unit tests (120 specs)
npm run test:docs # documentation tests
npm run test:integration # E2E integration scenarios
npm run test:all # full pipeline: test → docs → integration → knipContributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-adapter) - Write tests first (TDD) — see
src/test/for patterns - Implement your adapter extending
SocialAdapter - Ensure
npm run test:allpasses (all tests + knip) - Submit a Pull Request
License
ISC © nan•web
Part of the nan•web Sovereign Digital State ecosystem.