Files
daily-notification-plugin/contracts/NotificationContract.v1.ts
Matthew Raymer 114843edd5 feat(contract): Add Contract v1 freeze with schema and validation
- Add contracts/NotificationContract.v1.ts (frozen TypeScript interface)
- Add contracts/notification.v1.schema.json (JSON Schema)
- Add contracts/notification.v1.hash (SHA-256 hash for integrity)
- Add scripts/generate-contract-schema.js (schema generator)
- Add scripts/check-contract.sh (validation script)

Contract v1 is now frozen as single source of truth for cross-platform
notification content. All platforms must conform to this contract.

See docs/0017-Daily-Notification-Contract-Freeze.md for details.
2025-11-12 11:23:43 +00:00

134 lines
3.5 KiB
TypeScript

/**
* NotificationContract.v1.ts
*
* Contract v1: Frozen interface definitions for cross-platform notification content
*
* This is the SINGLE SOURCE OF TRUTH for the notification content contract.
* Both Android and iOS MUST conform to this exact interface.
*
* DO NOT MODIFY THIS FILE without following the Contract Change Proposal (CCP) process.
* See docs/0017-Daily-Notification-Contract-Freeze.md
*
* @author Matthew Raymer
* @version 1.0.0
* @frozen true
*/
/**
* Notification content item (v1)
*
* Core data structure for notifications that the plugin can schedule and display.
* All times are in epoch milliseconds.
*
* @contract v1 - DO NOT MODIFY without CCP
*/
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, optional) */
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 (v1)
*
* @contract v1 - DO NOT MODIFY without CCP
*/
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 (v1)
*
* @contract v1 - DO NOT MODIFY without CCP
*/
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>;
}
/**
* Scheduling policy configuration (v1)
*
* @contract v1 - DO NOT MODIFY without CCP
*/
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;
}
/**
* Contract metadata
*
* Used for runtime handshake and validation
*/
export const CONTRACT_V1 = {
version: 'v1',
schemaFile: 'contracts/notification.v1.schema.json',
hashFile: 'contracts/notification.v1.hash',
frozen: true,
createdAt: '2025-01-28'
} as const;