feat: add platform-specific configuration and build system

- Add Android configuration with notification channels and WorkManager
- Add iOS configuration with BGTaskScheduler and notification categories
- Add platform-specific build scripts and bundle size checking
- Add API change detection and type checksum validation
- Add release notes generation and chaos testing scripts
- Add Vite configuration for TimeSafari-specific builds
- Add Android notification channels XML configuration
- Update package.json with new build scripts and dependencies

Platforms: Android (WorkManager + SQLite), iOS (BGTaskScheduler + Core Data), Electron (Desktop notifications)
This commit is contained in:
Matthew Raymer
2025-10-08 06:18:32 +00:00
parent a4ad21856e
commit 79dd1d82a7
10 changed files with 1938 additions and 0 deletions

View File

@@ -0,0 +1,357 @@
/**
* TimeSafari Android Configuration
*
* Provides TimeSafari-specific Android platform configuration including
* notification channels, permissions, and battery optimization settings.
*
* @author Matthew Raymer
* @version 1.0.0
*/
/**
* TimeSafari Android Configuration Interface
*/
export interface TimeSafariAndroidConfig {
/**
* Notification channel configuration
*/
notificationChannels: NotificationChannelConfig[];
/**
* Permission requirements
*/
permissions: AndroidPermission[];
/**
* Battery optimization settings
*/
batteryOptimization: BatteryOptimizationConfig;
/**
* Doze and App Standby settings
*/
powerManagement: PowerManagementConfig;
/**
* WorkManager constraints
*/
workManagerConstraints: WorkManagerConstraints;
}
/**
* Notification Channel Configuration
*/
export interface NotificationChannelConfig {
id: string;
name: string;
description: string;
importance: 'low' | 'default' | 'high' | 'max';
enableLights: boolean;
enableVibration: boolean;
lightColor: string;
sound: string | null;
showBadge: boolean;
bypassDnd: boolean;
lockscreenVisibility: 'public' | 'private' | 'secret';
}
/**
* Android Permission Configuration
*/
export interface AndroidPermission {
name: string;
description: string;
required: boolean;
runtime: boolean;
category: 'notification' | 'alarm' | 'network' | 'storage' | 'system';
}
/**
* Battery Optimization Configuration
*/
export interface BatteryOptimizationConfig {
exemptPackages: string[];
whitelistRequestMessage: string;
optimizationCheckInterval: number; // minutes
fallbackBehavior: 'graceful' | 'aggressive' | 'disabled';
}
/**
* Power Management Configuration
*/
export interface PowerManagementConfig {
dozeModeHandling: 'ignore' | 'adapt' | 'request_whitelist';
appStandbyHandling: 'ignore' | 'adapt' | 'request_whitelist';
backgroundRestrictions: 'ignore' | 'adapt' | 'request_whitelist';
adaptiveBatteryHandling: 'ignore' | 'adapt' | 'request_whitelist';
}
/**
* WorkManager Constraints Configuration
*/
export interface WorkManagerConstraints {
networkType: 'not_required' | 'connected' | 'unmetered' | 'not_roaming' | 'metered';
requiresBatteryNotLow: boolean;
requiresCharging: boolean;
requiresDeviceIdle: boolean;
requiresStorageNotLow: boolean;
backoffPolicy: 'linear' | 'exponential';
backoffDelay: number; // milliseconds
maxRetries: number;
}
/**
* Default TimeSafari Android Configuration
*/
export const DEFAULT_TIMESAFARI_ANDROID_CONFIG: TimeSafariAndroidConfig = {
notificationChannels: [
{
id: 'timesafari_community_updates',
name: 'TimeSafari Community Updates',
description: 'Daily updates from your TimeSafari community including new offers, project updates, and trust network activities',
importance: 'default',
enableLights: true,
enableVibration: true,
lightColor: '#2196F3',
sound: 'default',
showBadge: true,
bypassDnd: false,
lockscreenVisibility: 'public'
},
{
id: 'timesafari_project_notifications',
name: 'TimeSafari Project Notifications',
description: 'Notifications about starred projects, funding updates, and project milestones',
importance: 'high',
enableLights: true,
enableVibration: true,
lightColor: '#4CAF50',
sound: 'default',
showBadge: true,
bypassDnd: false,
lockscreenVisibility: 'public'
},
{
id: 'timesafari_trust_network',
name: 'TimeSafari Trust Network',
description: 'Trust network activities, endorsements, and community recommendations',
importance: 'default',
enableLights: true,
enableVibration: false,
lightColor: '#FF9800',
sound: null,
showBadge: true,
bypassDnd: false,
lockscreenVisibility: 'public'
},
{
id: 'timesafari_system',
name: 'TimeSafari System',
description: 'System notifications, authentication updates, and plugin status messages',
importance: 'low',
enableLights: false,
enableVibration: false,
lightColor: '#9E9E9E',
sound: null,
showBadge: false,
bypassDnd: false,
lockscreenVisibility: 'private'
},
{
id: 'timesafari_reminders',
name: 'TimeSafari Reminders',
description: 'Personal reminders and daily check-ins for your TimeSafari activities',
importance: 'default',
enableLights: true,
enableVibration: true,
lightColor: '#9C27B0',
sound: 'default',
showBadge: true,
bypassDnd: false,
lockscreenVisibility: 'public'
}
],
permissions: [
{
name: 'android.permission.POST_NOTIFICATIONS',
description: 'Allow TimeSafari to show notifications',
required: true,
runtime: true,
category: 'notification'
},
{
name: 'android.permission.SCHEDULE_EXACT_ALARM',
description: 'Allow TimeSafari to schedule exact alarms for notifications',
required: true,
runtime: true,
category: 'alarm'
},
{
name: 'android.permission.USE_EXACT_ALARM',
description: 'Allow TimeSafari to use exact alarms',
required: false,
runtime: false,
category: 'alarm'
},
{
name: 'android.permission.WAKE_LOCK',
description: 'Allow TimeSafari to keep device awake for background tasks',
required: true,
runtime: false,
category: 'system'
},
{
name: 'android.permission.RECEIVE_BOOT_COMPLETED',
description: 'Allow TimeSafari to restart notifications after device reboot',
required: true,
runtime: false,
category: 'system'
},
{
name: 'android.permission.INTERNET',
description: 'Allow TimeSafari to fetch community data and send callbacks',
required: true,
runtime: false,
category: 'network'
},
{
name: 'android.permission.ACCESS_NETWORK_STATE',
description: 'Allow TimeSafari to check network connectivity',
required: true,
runtime: false,
category: 'network'
}
],
batteryOptimization: {
exemptPackages: ['com.timesafari.dailynotification'],
whitelistRequestMessage: 'TimeSafari needs to run in the background to deliver your daily community updates and notifications. Please whitelist TimeSafari from battery optimization.',
optimizationCheckInterval: 60, // 1 hour
fallbackBehavior: 'graceful'
},
powerManagement: {
dozeModeHandling: 'request_whitelist',
appStandbyHandling: 'request_whitelist',
backgroundRestrictions: 'request_whitelist',
adaptiveBatteryHandling: 'request_whitelist'
},
workManagerConstraints: {
networkType: 'connected',
requiresBatteryNotLow: false,
requiresCharging: false,
requiresDeviceIdle: false,
requiresStorageNotLow: true,
backoffPolicy: 'exponential',
backoffDelay: 30000, // 30 seconds
maxRetries: 3
}
};
/**
* TimeSafari Android Configuration Manager
*/
export class TimeSafariAndroidConfigManager {
private config: TimeSafariAndroidConfig;
constructor(config?: Partial<TimeSafariAndroidConfig>) {
this.config = { ...DEFAULT_TIMESAFARI_ANDROID_CONFIG, ...config };
}
/**
* Get notification channel configuration
*/
getNotificationChannel(channelId: string): NotificationChannelConfig | undefined {
return this.config.notificationChannels.find(channel => channel.id === channelId);
}
/**
* Get all notification channels
*/
getAllNotificationChannels(): NotificationChannelConfig[] {
return this.config.notificationChannels;
}
/**
* Get required permissions
*/
getRequiredPermissions(): AndroidPermission[] {
return this.config.permissions.filter(permission => permission.required);
}
/**
* Get runtime permissions
*/
getRuntimePermissions(): AndroidPermission[] {
return this.config.permissions.filter(permission => permission.runtime);
}
/**
* Get permissions by category
*/
getPermissionsByCategory(category: string): AndroidPermission[] {
return this.config.permissions.filter(permission => permission.category === category);
}
/**
* Get battery optimization configuration
*/
getBatteryOptimizationConfig(): BatteryOptimizationConfig {
return this.config.batteryOptimization;
}
/**
* Get power management configuration
*/
getPowerManagementConfig(): PowerManagementConfig {
return this.config.powerManagement;
}
/**
* Get WorkManager constraints
*/
getWorkManagerConstraints(): WorkManagerConstraints {
return this.config.workManagerConstraints;
}
/**
* Update configuration
*/
updateConfig(updates: Partial<TimeSafariAndroidConfig>): void {
this.config = { ...this.config, ...updates };
}
/**
* Validate configuration
*/
validateConfig(): { valid: boolean; errors: string[] } {
const errors: string[] = [];
// Validate notification channels
if (this.config.notificationChannels.length === 0) {
errors.push('At least one notification channel must be configured');
}
// Validate permissions
const requiredPermissions = this.getRequiredPermissions();
if (requiredPermissions.length === 0) {
errors.push('At least one required permission must be configured');
}
// Validate WorkManager constraints
if (this.config.workManagerConstraints.maxRetries < 0) {
errors.push('WorkManager maxRetries must be non-negative');
}
if (this.config.workManagerConstraints.backoffDelay < 0) {
errors.push('WorkManager backoffDelay must be non-negative');
}
return {
valid: errors.length === 0,
errors
};
}
}

View File

@@ -0,0 +1,537 @@
/**
* TimeSafari iOS Configuration
*
* Provides TimeSafari-specific iOS platform configuration including
* notification categories, background tasks, and permission handling.
*
* @author Matthew Raymer
* @version 1.0.0
*/
/**
* TimeSafari iOS Configuration Interface
*/
export interface TimeSafariIOSConfig {
/**
* Notification categories configuration
*/
notificationCategories: NotificationCategoryConfig[];
/**
* Background task configuration
*/
backgroundTasks: BackgroundTaskConfig[];
/**
* Permission configuration
*/
permissions: IOSPermission[];
/**
* Background App Refresh settings
*/
backgroundAppRefresh: BackgroundAppRefreshConfig;
/**
* Notification center settings
*/
notificationCenter: NotificationCenterConfig;
}
/**
* Notification Category Configuration
*/
export interface NotificationCategoryConfig {
identifier: string;
actions: NotificationActionConfig[];
intentIdentifiers: string[];
hiddenPreviewsBodyPlaceholder: string;
categorySummaryFormat: string;
options: NotificationCategoryOptions;
}
/**
* Notification Action Configuration
*/
export interface NotificationActionConfig {
identifier: string;
title: string;
options: NotificationActionOptions;
textInputButtonTitle?: string;
textInputPlaceholder?: string;
}
/**
* Background Task Configuration
*/
export interface BackgroundTaskConfig {
identifier: string;
name: string;
description: string;
estimatedDuration: number; // seconds
requiresNetworkConnectivity: boolean;
requiresExternalPower: boolean;
priority: 'low' | 'default' | 'high';
}
/**
* iOS Permission Configuration
*/
export interface IOSPermission {
type: 'notifications' | 'backgroundAppRefresh' | 'backgroundProcessing';
description: string;
required: boolean;
provisional: boolean;
options: NotificationPermissionOptions;
}
/**
* Background App Refresh Configuration
*/
export interface BackgroundAppRefreshConfig {
enabled: boolean;
allowedInLowPowerMode: boolean;
allowedInBackground: boolean;
minimumInterval: number; // seconds
maximumDuration: number; // seconds
}
/**
* Notification Center Configuration
*/
export interface NotificationCenterConfig {
delegateClassName: string;
categories: string[];
settings: NotificationCenterSettings;
}
/**
* Notification Category Options
*/
export interface NotificationCategoryOptions {
customDismissAction: boolean;
allowInCarPlay: boolean;
allowAnnouncement: boolean;
showTitle: boolean;
showSubtitle: boolean;
showBody: boolean;
}
/**
* Notification Action Options
*/
export interface NotificationActionOptions {
foreground: boolean;
destructive: boolean;
authenticationRequired: boolean;
}
/**
* Notification Permission Options
*/
export interface NotificationPermissionOptions {
alert: boolean;
badge: boolean;
sound: boolean;
carPlay: boolean;
criticalAlert: boolean;
provisional: boolean;
providesAppNotificationSettings: boolean;
announcement: boolean;
}
/**
* Notification Center Settings
*/
export interface NotificationCenterSettings {
authorizationStatus: 'notDetermined' | 'denied' | 'authorized' | 'provisional';
alertSetting: 'notSupported' | 'disabled' | 'enabled';
badgeSetting: 'notSupported' | 'disabled' | 'enabled';
soundSetting: 'notSupported' | 'disabled' | 'enabled';
carPlaySetting: 'notSupported' | 'disabled' | 'enabled';
criticalAlertSetting: 'notSupported' | 'disabled' | 'enabled';
providesAppNotificationSettings: boolean;
announcementSetting: 'notSupported' | 'disabled' | 'enabled';
}
/**
* Default TimeSafari iOS Configuration
*/
export const DEFAULT_TIMESAFARI_IOS_CONFIG: TimeSafariIOSConfig = {
notificationCategories: [
{
identifier: 'TIMESAFARI_COMMUNITY_UPDATE',
actions: [
{
identifier: 'VIEW_OFFERS',
title: 'View Offers',
options: {
foreground: true,
destructive: false,
authenticationRequired: false
}
},
{
identifier: 'VIEW_PROJECTS',
title: 'View Projects',
options: {
foreground: true,
destructive: false,
authenticationRequired: false
}
},
{
identifier: 'DISMISS',
title: 'Dismiss',
options: {
foreground: false,
destructive: false,
authenticationRequired: false
}
}
],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: 'New TimeSafari community update available',
categorySummaryFormat: '%u more community updates',
options: {
customDismissAction: true,
allowInCarPlay: false,
allowAnnouncement: true,
showTitle: true,
showSubtitle: true,
showBody: true
}
},
{
identifier: 'TIMESAFARI_PROJECT_UPDATE',
actions: [
{
identifier: 'VIEW_PROJECT',
title: 'View Project',
options: {
foreground: true,
destructive: false,
authenticationRequired: false
}
},
{
identifier: 'STAR_PROJECT',
title: 'Star Project',
options: {
foreground: false,
destructive: false,
authenticationRequired: false
}
},
{
identifier: 'DISMISS',
title: 'Dismiss',
options: {
foreground: false,
destructive: false,
authenticationRequired: false
}
}
],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: 'New project update available',
categorySummaryFormat: '%u more project updates',
options: {
customDismissAction: true,
allowInCarPlay: false,
allowAnnouncement: true,
showTitle: true,
showSubtitle: true,
showBody: true
}
},
{
identifier: 'TIMESAFARI_TRUST_NETWORK',
actions: [
{
identifier: 'VIEW_ENDORSEMENT',
title: 'View Endorsement',
options: {
foreground: true,
destructive: false,
authenticationRequired: false
}
},
{
identifier: 'DISMISS',
title: 'Dismiss',
options: {
foreground: false,
destructive: false,
authenticationRequired: false
}
}
],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: 'New trust network activity',
categorySummaryFormat: '%u more trust network updates',
options: {
customDismissAction: true,
allowInCarPlay: false,
allowAnnouncement: false,
showTitle: true,
showSubtitle: true,
showBody: true
}
},
{
identifier: 'TIMESAFARI_REMINDER',
actions: [
{
identifier: 'COMPLETE_TASK',
title: 'Complete Task',
options: {
foreground: true,
destructive: false,
authenticationRequired: false
}
},
{
identifier: 'SNOOZE',
title: 'Snooze 1 Hour',
options: {
foreground: false,
destructive: false,
authenticationRequired: false
}
},
{
identifier: 'DISMISS',
title: 'Dismiss',
options: {
foreground: false,
destructive: false,
authenticationRequired: false
}
}
],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: 'TimeSafari reminder',
categorySummaryFormat: '%u more reminders',
options: {
customDismissAction: true,
allowInCarPlay: true,
allowAnnouncement: true,
showTitle: true,
showSubtitle: true,
showBody: true
}
}
],
backgroundTasks: [
{
identifier: 'com.timesafari.dailynotification.fetch',
name: 'TimeSafari Content Fetch',
description: 'Fetch daily community content and project updates',
estimatedDuration: 30,
requiresNetworkConnectivity: true,
requiresExternalPower: false,
priority: 'default'
},
{
identifier: 'com.timesafari.dailynotification.notify',
name: 'TimeSafari Notification Delivery',
description: 'Deliver scheduled notifications to user',
estimatedDuration: 10,
requiresNetworkConnectivity: false,
requiresExternalPower: false,
priority: 'high'
}
],
permissions: [
{
type: 'notifications',
description: 'Allow TimeSafari to send notifications about community updates and project activities',
required: true,
provisional: true,
options: {
alert: true,
badge: true,
sound: true,
carPlay: false,
criticalAlert: false,
provisional: true,
providesAppNotificationSettings: true,
announcement: true
}
},
{
type: 'backgroundAppRefresh',
description: 'Allow TimeSafari to refresh content in the background',
required: true,
provisional: false,
options: {
alert: false,
badge: false,
sound: false,
carPlay: false,
criticalAlert: false,
provisional: false,
providesAppNotificationSettings: false,
announcement: false
}
},
{
type: 'backgroundProcessing',
description: 'Allow TimeSafari to process background tasks',
required: true,
provisional: false,
options: {
alert: false,
badge: false,
sound: false,
carPlay: false,
criticalAlert: false,
provisional: false,
providesAppNotificationSettings: false,
announcement: false
}
}
],
backgroundAppRefresh: {
enabled: true,
allowedInLowPowerMode: false,
allowedInBackground: true,
minimumInterval: 300, // 5 minutes
maximumDuration: 30 // 30 seconds
},
notificationCenter: {
delegateClassName: 'TimeSafariNotificationCenterDelegate',
categories: [
'TIMESAFARI_COMMUNITY_UPDATE',
'TIMESAFARI_PROJECT_UPDATE',
'TIMESAFARI_TRUST_NETWORK',
'TIMESAFARI_REMINDER'
],
settings: {
authorizationStatus: 'notDetermined',
alertSetting: 'enabled',
badgeSetting: 'enabled',
soundSetting: 'enabled',
carPlaySetting: 'disabled',
criticalAlertSetting: 'disabled',
providesAppNotificationSettings: true,
announcementSetting: 'enabled'
}
}
};
/**
* TimeSafari iOS Configuration Manager
*/
export class TimeSafariIOSConfigManager {
private config: TimeSafariIOSConfig;
constructor(config?: Partial<TimeSafariIOSConfig>) {
this.config = { ...DEFAULT_TIMESAFARI_IOS_CONFIG, ...config };
}
/**
* Get notification category configuration
*/
getNotificationCategory(categoryId: string): NotificationCategoryConfig | undefined {
return this.config.notificationCategories.find(category => category.identifier === categoryId);
}
/**
* Get all notification categories
*/
getAllNotificationCategories(): NotificationCategoryConfig[] {
return this.config.notificationCategories;
}
/**
* Get background task configuration
*/
getBackgroundTask(taskId: string): BackgroundTaskConfig | undefined {
return this.config.backgroundTasks.find(task => task.identifier === taskId);
}
/**
* Get all background tasks
*/
getAllBackgroundTasks(): BackgroundTaskConfig[] {
return this.config.backgroundTasks;
}
/**
* Get required permissions
*/
getRequiredPermissions(): IOSPermission[] {
return this.config.permissions.filter(permission => permission.required);
}
/**
* Get provisional permissions
*/
getProvisionalPermissions(): IOSPermission[] {
return this.config.permissions.filter(permission => permission.provisional);
}
/**
* Get background app refresh configuration
*/
getBackgroundAppRefreshConfig(): BackgroundAppRefreshConfig {
return this.config.backgroundAppRefresh;
}
/**
* Get notification center configuration
*/
getNotificationCenterConfig(): NotificationCenterConfig {
return this.config.notificationCenter;
}
/**
* Update configuration
*/
updateConfig(updates: Partial<TimeSafariIOSConfig>): void {
this.config = { ...this.config, ...updates };
}
/**
* Validate configuration
*/
validateConfig(): { valid: boolean; errors: string[] } {
const errors: string[] = [];
// Validate notification categories
if (this.config.notificationCategories.length === 0) {
errors.push('At least one notification category must be configured');
}
// Validate background tasks
if (this.config.backgroundTasks.length === 0) {
errors.push('At least one background task must be configured');
}
// Validate permissions
const requiredPermissions = this.getRequiredPermissions();
if (requiredPermissions.length === 0) {
errors.push('At least one required permission must be configured');
}
// Validate background app refresh
if (this.config.backgroundAppRefresh.minimumInterval < 0) {
errors.push('Background app refresh minimum interval must be non-negative');
}
if (this.config.backgroundAppRefresh.maximumDuration < 0) {
errors.push('Background app refresh maximum duration must be non-negative');
}
return {
valid: errors.length === 0,
errors
};
}
}