rspace-online/modules/rfeeds/schemas.ts

158 lines
3.4 KiB
TypeScript

/**
* rFeeds Automerge document schemas.
*
* Granularity: one Automerge document per space.
* DocId format: {space}:rfeeds:data
*/
import type { DocSchema } from '../../shared/local-first/document';
// ── Feed source ──
export interface FeedSource {
id: string;
url: string;
name: string;
color: string;
enabled: boolean;
lastFetchedAt: number;
lastError: string | null;
itemCount: number;
intervalMs: number; // default 300_000 (5 min)
addedBy: string; // DID of who added it
addedAt: number;
}
// ── Imported feed item ──
export interface FeedItem {
id: string;
sourceId: string;
guid: string;
title: string;
url: string;
summary: string; // 500 chars max, HTML stripped
author: string;
publishedAt: number;
importedAt: number;
reshared: boolean; // included in outbound Atom feed
}
// ── Manual post ──
export interface ManualPost {
id: string;
content: string;
authorDid: string;
authorName: string;
createdAt: number;
updatedAt: number;
}
// ── Activity cache (Phase 2) ──
export interface ActivityCacheEntry {
id: string; // deterministic: `${moduleId}:${space}:${itemId}`
moduleId: string; // 'rcal' | 'rtasks' | 'rdocs' | etc
itemType: string; // 'event' | 'task' | 'note' | 'thread' | 'file' | etc
title: string;
summary: string; // 280 char max
url: string; // e.g. https://demo.rspace.online/rcal
author: string;
publishedAt: number;
reshared: boolean;
syncedAt: number;
}
export interface ActivityModuleConfig {
enabled: boolean;
label: string;
color: string;
}
// ── User feed profiles (Phase 2) ──
export interface UserFeedProfile {
did: string;
displayName: string;
bio: string;
publishModules: string[]; // which moduleIds appear in personal feed
createdAt: number;
updatedAt: number;
}
// ── Settings ──
export interface FeedsSettings {
feedTitle: string;
feedDescription: string;
activityModules: Record<string, ActivityModuleConfig>;
}
// ── Document root ──
export interface FeedsDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
sources: Record<string, FeedSource>;
items: Record<string, FeedItem>;
posts: Record<string, ManualPost>;
activityCache: Record<string, ActivityCacheEntry>;
userProfiles: Record<string, UserFeedProfile>;
settings: FeedsSettings;
}
// ── Schema registration ──
export const feedsSchema: DocSchema<FeedsDoc> = {
module: 'rfeeds',
collection: 'data',
version: 2,
init: (): FeedsDoc => ({
meta: {
module: 'rfeeds',
collection: 'data',
version: 2,
spaceSlug: '',
createdAt: Date.now(),
},
sources: {},
items: {},
posts: {},
activityCache: {},
userProfiles: {},
settings: {
feedTitle: '',
feedDescription: '',
activityModules: {},
},
}),
migrate: (doc: any): any => {
if (!doc.activityCache) doc.activityCache = {};
if (!doc.userProfiles) doc.userProfiles = {};
if (!doc.settings.activityModules) doc.settings.activityModules = {};
if (doc.meta) doc.meta.version = 2;
return doc;
},
};
// ── Helpers ──
export function feedsDocId(space: string) {
return `${space}:rfeeds:data` as const;
}
/** Max items retained per source */
export const MAX_ITEMS_PER_SOURCE = 200;
/** Max activity cache entries */
export const MAX_ACTIVITY_CACHE = 500;
/** Default sync interval */
export const DEFAULT_SYNC_INTERVAL_MS = 5 * 60 * 1000;