docs(refactor): add integration point refactor analysis and implementation plan
Add comprehensive documentation and implementation artifacts for refactoring the plugin to use app-provided content fetchers instead of hardcoded TimeSafari integration. Changes: - Add integration-point-refactor-analysis.md with complete ADR, interfaces, migration plan, and 7-PR breakdown - Add INTEGRATION_REFACTOR_QUICK_START.md for quick reference on new machines - Add src/types/content-fetcher.ts with TypeScript SPI interfaces - Add examples/native-fetcher-android.kt with Kotlin implementation skeleton - Add examples/js-fetcher-typescript.ts with TypeScript implementation skeleton - Add tests/fixtures/test-contract.json for golden contract testing Architecture Decisions: - Dual-path SPI: Native Fetcher (background) + JS Fetcher (foreground only) - Background reliability: Native SPI only, no JS bridging in workers - Reversibility: Legacy code behind feature flag for one minor release - Test contract: Single JSON fixture for both fetcher paths This provides complete specification for implementing the refactor in 7 PRs, starting with SPI shell and progressing through background workers, deduplication, failure policies, and finally legacy code removal. All documentation is self-contained and ready for implementation on any machine.
This commit is contained in:
135
src/types/content-fetcher.ts
Normal file
135
src/types/content-fetcher.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Content Fetcher SPI Types
|
||||
*
|
||||
* TypeScript definitions for the content fetcher Service Provider Interface
|
||||
* that allows host apps to provide notification content fetching logic.
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Notification content item
|
||||
*
|
||||
* Core data structure for notifications that the plugin can schedule
|
||||
* and display. All TimeSafari-specific data must be converted to this
|
||||
* generic format by the host app's fetcher implementation.
|
||||
*/
|
||||
export interface NotificationContent {
|
||||
/** Unique identifier for this notification */
|
||||
id: string;
|
||||
|
||||
/** Notification title (required) */
|
||||
title: string;
|
||||
|
||||
/** Notification body text (optional) */
|
||||
body?: string;
|
||||
|
||||
/** When this notification should be displayed (epoch ms) */
|
||||
scheduledTime?: number;
|
||||
|
||||
/** When this content was fetched (epoch ms, required) */
|
||||
fetchTime: number;
|
||||
|
||||
/** Optional image URL for rich notifications */
|
||||
mediaUrl?: string;
|
||||
|
||||
/** Cache TTL in seconds (how long this content is valid) */
|
||||
ttlSeconds?: number;
|
||||
|
||||
/** Deduplication key (for idempotency) */
|
||||
dedupeKey?: string;
|
||||
|
||||
/** Notification priority level */
|
||||
priority?: 'min' | 'low' | 'default' | 'high' | 'max';
|
||||
|
||||
/** Additional metadata (opaque to plugin) */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reason why content fetch was triggered
|
||||
*/
|
||||
export type FetchTrigger =
|
||||
| 'background_work' // Background worker (WorkManager/BGTask)
|
||||
| 'prefetch' // Prefetch before scheduled notification
|
||||
| 'manual' // User-initiated refresh
|
||||
| 'scheduled'; // Scheduled fetch
|
||||
|
||||
/**
|
||||
* Context provided to fetcher about why fetch was triggered
|
||||
*/
|
||||
export interface FetchContext {
|
||||
/** Why the fetch was triggered */
|
||||
trigger: FetchTrigger;
|
||||
|
||||
/** When notification is scheduled (if applicable, epoch ms) */
|
||||
scheduledTime?: number;
|
||||
|
||||
/** When fetch was triggered (epoch ms) */
|
||||
fetchTime: number;
|
||||
|
||||
/** Additional context from plugin (opaque) */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript Content Fetcher Interface
|
||||
*
|
||||
* Host app implements this interface to provide notification content
|
||||
* fetching logic. This is used ONLY for foreground/manual refresh.
|
||||
*
|
||||
* Background workers use Native Fetcher SPI (Kotlin/Swift) for reliability.
|
||||
*/
|
||||
export interface JsNotificationContentFetcher {
|
||||
/**
|
||||
* Fetch notification content from external source
|
||||
*
|
||||
* Called by plugin when:
|
||||
* - Manual refresh is requested
|
||||
* - Prefetch is needed while app is foregrounded
|
||||
* - Testing/debugging
|
||||
*
|
||||
* NOT called from background workers (they use Native SPI)
|
||||
*
|
||||
* @param context Context about why fetch was triggered
|
||||
* @returns Promise with array of notification content
|
||||
*/
|
||||
fetchContent(context: FetchContext): Promise<NotificationContent[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduling policy configuration
|
||||
*
|
||||
* Controls how the plugin schedules fetches, handles retries,
|
||||
* and manages deduplication.
|
||||
*/
|
||||
export interface SchedulingPolicy {
|
||||
/** How early to prefetch before scheduled notification (ms) */
|
||||
prefetchWindowMs?: number;
|
||||
|
||||
/** Retry backoff configuration */
|
||||
retryBackoff: {
|
||||
/** Minimum delay between retries (ms) */
|
||||
minMs: number;
|
||||
/** Maximum delay between retries (ms) */
|
||||
maxMs: number;
|
||||
/** Exponential backoff multiplier */
|
||||
factor: number;
|
||||
/** Jitter percentage (0-100) */
|
||||
jitterPct: number;
|
||||
};
|
||||
|
||||
/** Maximum items to fetch per batch */
|
||||
maxBatchSize?: number;
|
||||
|
||||
/** Deduplication window (ms) - prevents duplicate notifications */
|
||||
dedupeHorizonMs?: number;
|
||||
|
||||
/** Default cache TTL if item doesn't specify (seconds) */
|
||||
cacheTtlSeconds?: number;
|
||||
|
||||
/** Whether exact alarms are allowed (Android 12+) */
|
||||
exactAlarmsAllowed?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user