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:
Matthew Raymer
2025-10-29 13:04:49 +00:00
parent ed5dcfbbd1
commit e83b1518d7
6 changed files with 1635 additions and 0 deletions

View 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;
}