Something went wrong. Try again.
A social knowledge tool for research trails, built on ATProto semble.so
research-tool social-network tools-for-thought atproto
Something went wrong. Try again.
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233// DTOs for Product Analytics (Weekly Active Curators + Activation Funnel)
export interface AnalyticsWeekOptions { /** * ISO date string. Resolved to the ISO week (Mon–Sun, UTC) it falls in and * used as the most recent week in the returned series. * Omitted => the most recent COMPLETED week (the current in-progress week is * excluded). */ endWeek?: string; /** * Number of weeks of history ending at (and including) the end week. * 2 => current week + prior week (for a comparison view). * 0 (or undefined) => all-time (no lower bound). */ weeks?: number;}
// Weekly Active Curators (WAC)
export interface WacDataPoint { weekStart: string; // ISO date of the week's Monday (date_trunc('week')) collectionOrConnection: number; // distinct users who added-to-collection OR connected collectionAdd: number; // distinct users who added a card to any collection connection: number; // distinct users who created a connection othersCollectionAdd: number; // distinct users who added to SOMEONE ELSE'S collection}
export interface WacStatsDTO { dataPoints: WacDataPoint[]; // chronological, oldest -> newest, dense (gap-filled) periodStart: string; periodEnd: string;}
// Activation Funnel (weekly signup cohorts)
export interface ActivationFunnelDataPoint { cohortWeekStart: string; // ISO Monday of the signup week signups: number; // rung 0: users whose linked_at is in this week savedUrlCard7d: number; // rung 1: saved a type='URL' card within 7d of linked_at curated14d: number; // rung 2: collection-add OR connection within 14d notified30d: number; // rung 3: received a notification within 30d}
export interface ActivationFunnelStatsDTO { dataPoints: ActivationFunnelDataPoint[]; // chronological, oldest -> newest, dense (gap-filled) periodStart: string; periodEnd: string;}
// Retention (weekly signup cohorts, calendar-week anchored)
export interface RetentionWeekCell { /** Calendar weeks after the cohort week (1..N; offset 0 is the funnel's job). */ weekOffset: number; /** Distinct cohort users with ANY activity (card, collection, collection-add, connection, follow) that week. */ activeUsers: number; /** Distinct cohort users who added to a collection OR created a connection that week (WAC definition). */ curatingUsers: number;}
export interface RetentionCohortDataPoint { cohortWeekStart: string; // ISO Monday of the signup week cohortSize: number; // users whose linked_at is in this week (minus internal accounts) /** Dense, offset 1..N where N = completed calendar weeks since the cohort week. */ weeks: RetentionWeekCell[];}
export interface RetentionStatsDTO { dataPoints: RetentionCohortDataPoint[]; // chronological, oldest -> newest, dense (gap-filled) periodStart: string; periodEnd: string;}
// Segmented retention (cohorts pooled across the range, split by a user attribute)
export type RetentionSegmentBy = 'onboardingState' | 'notifiedFirstWeek';
export interface RetentionSegmentWeekCell { weekOffset: number; // 1..N (N = offsets reachable by the oldest cohort in range) /** Segment users signed up early enough for this offset to be a completed week — the rate denominator. */ eligibleUsers: number; activeUsers: number; // of eligible, distinct users with any activity at this offset curatingUsers: number; // of eligible, distinct users with collection-add or connection at this offset}
export interface RetentionSegmentDataPoint { /** Segment value, e.g. 'COMPLETED' / 'SKIPPED' / 'NONE', or 'notified' / 'not_notified'. */ segment: string; userCount: number; // segment users in the pooled cohort range weeks: RetentionSegmentWeekCell[];}
export interface RetentionSegmentsStatsDTO { segmentBy: RetentionSegmentBy; dataPoints: RetentionSegmentDataPoint[]; // sorted by userCount desc, then segment asc periodStart: string; // pooled cohort range periodEnd: string;}
// API usage (per-client-source request analytics over api_request_logs)
export interface ApiUsageSourceWeekly { source: string; // 'mcp' | 'extension' | 'api' | future client identifiers users: number; // distinct users who called this week via this source calls: number; // total requests this week via this source}
export interface ApiUsageDataPoint { weekStart: string; // ISO date of the week's Monday (date_trunc('week')) sources: ApiUsageSourceWeekly[]; // sorted by calls desc; empty when no traffic}
export interface ApiUsageEndpointStat { method: string; endpoint: string; // route pattern, e.g. /xrpc/cards/:id calls: number; users: number;}
export interface ApiUsageSourceTotals { source: string; users: number; // distinct users over the whole period calls: number; // total requests over the whole period topEndpoints: ApiUsageEndpointStat[]; // top 10 by calls desc}
export interface ApiUsageStatsDTO { dataPoints: ApiUsageDataPoint[]; // chronological, oldest -> newest, dense (gap-filled) totals: ApiUsageSourceTotals[]; // sorted by calls desc periodStart: string; periodEnd: string;}
// Onboarding stats (single weekly signup cohort, or all-time summary)
/** onboarding_state text[] columns broken down per stored value. */export type OnboardingArrayDimension = | 'topicsSelected' | 'linksSuggested' | 'linksSelected' | 'suggestedAccounts' | 'suggestedCollections' | 'followedAccounts' | 'followedCollections' | 'firstCards' | 'intention' | 'referralSource';
/** onboarding_state nullable timestamp columns counted by presence. */export type OnboardingMilestoneDimension = | 'pwaClicked' | 'iosShortcutClicked' | 'browserExtensionClicked' | 'mcpClicked' | 'saveModalGuideCompleted' | 'connectionCreationModalCompleted' | 'semblePageNavigationCompleted';
export interface OnboardingDimensionCountsRaw { /** Users since the onboarding launch date with a non-empty value. */ totalUserCount: number; /** Subset who signed up in the cohort week (0 in summary mode). */ weeklyUserCount: number; /** DIDs of the cohort-week users (empty in summary mode). */ weeklyUserIds: string[];}
export interface OnboardingValueStatRaw extends OnboardingDimensionCountsRaw { /** Raw stored string: topic, URL, DID, or UUID depending on the dimension. */ value: string;}
/** A cohort-week user's first_collection / first_connection value. */export interface OnboardingSingleValueRaw { userId: string; value: string;}
export interface OnboardingStatsRaw { /** ISO Monday of the cohort week; null in summary mode. */ cohortWeekStart: string | null; totalNewUserCount: number; weeklyNewUsersCount: number; onboardingState: Array<{ state: string } & OnboardingDimensionCountsRaw>; dimensions: Record< OnboardingArrayDimension, OnboardingDimensionCountsRaw & { stats: OnboardingValueStatRaw[] } >; firstCollection: OnboardingDimensionCountsRaw & { weeklyValues: OnboardingSingleValueRaw[]; }; firstConnection: OnboardingDimensionCountsRaw & { weeklyValues: OnboardingSingleValueRaw[]; }; milestones: Record< OnboardingMilestoneDimension, OnboardingDimensionCountsRaw >;}
/** * Read-only query repository for product-analytics dashboards. * * WAC and activation-funnel return a dense weekly series. A "current vs prior * week" comparison is just `weeks: 2` (the caller picks the last two points). * * Funnel rungs are counted INDEPENDENTLY against each weekly signup cohort — * they are not monotonically nested. Percentages are derived by the caller * from these absolute counts. * * The onboarding methods return ID-based raw stats (DIDs / UUIDs / raw * strings); hydration to profiles/collections/connections happens in the * application layer (OnboardingStatsComposer). In weekly mode, per-value * breakdowns include only values present in the cohort week, but each value's * totalUserCount still spans all users since the onboarding launch date. */export interface IProductAnalyticsQueryRepository { getWacStats(options: AnalyticsWeekOptions): Promise<WacStatsDTO>; getApiUsageStats(options: AnalyticsWeekOptions): Promise<ApiUsageStatsDTO>; getActivationFunnelStats( options: AnalyticsWeekOptions, ): Promise<ActivationFunnelStatsDTO>; getRetentionStats(options: AnalyticsWeekOptions): Promise<RetentionStatsDTO>; getRetentionSegmentsStats( options: AnalyticsWeekOptions & { segmentBy: RetentionSegmentBy }, ): Promise<RetentionSegmentsStatsDTO>; getOnboardingWeeklyStatsRaw(options: { endWeek?: string; }): Promise<OnboardingStatsRaw>; getOnboardingSummaryStatsRaw(): Promise<OnboardingStatsRaw>;}