@mywallpaper/addon-sdk 2.3.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +30 -4
- package/dist/index.d.ts +30 -4
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/dist/manifest.d.mts +30 -4
- package/dist/manifest.d.ts +30 -4
- package/dist/manifest.js +3 -0
- package/dist/manifest.js.map +1 -1
- package/dist/manifest.mjs +3 -0
- package/dist/manifest.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @mywallpaper/addon-sdk - TypeScript Types v2.
|
|
2
|
+
* @mywallpaper/addon-sdk - TypeScript Types v2.4
|
|
3
3
|
*
|
|
4
4
|
* Complete type definitions for building MyWallpaper addons.
|
|
5
5
|
* These types describe the runtime API available via window.MyWallpaper
|
|
@@ -72,7 +72,7 @@ type OAuthPermissionType = 'oauth:github' | 'oauth:google' | 'oauth:discord' | '
|
|
|
72
72
|
* Permission types that addons can request.
|
|
73
73
|
* Requested via `window.MyWallpaper.requestPermission(type, reason)`
|
|
74
74
|
*/
|
|
75
|
-
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | OAuthPermissionType;
|
|
75
|
+
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | 'same-origin' | OAuthPermissionType;
|
|
76
76
|
/**
|
|
77
77
|
* Constraints that may be applied to granted permissions.
|
|
78
78
|
* Returned when permission is granted with limitations.
|
|
@@ -418,7 +418,7 @@ interface MyWallpaperAPI {
|
|
|
418
418
|
/**
|
|
419
419
|
* SDK version.
|
|
420
420
|
*/
|
|
421
|
-
readonly version: '2.
|
|
421
|
+
readonly version: '2.4.0';
|
|
422
422
|
/**
|
|
423
423
|
* User language (detected by host).
|
|
424
424
|
*/
|
|
@@ -607,7 +607,7 @@ type AddonValues = Record<string, unknown>;
|
|
|
607
607
|
/**
|
|
608
608
|
* Available setting types for addon configuration.
|
|
609
609
|
*/
|
|
610
|
-
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
610
|
+
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'video' | 'audio' | 'file' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
611
611
|
/**
|
|
612
612
|
* Definition for a single addon setting.
|
|
613
613
|
*
|
|
@@ -776,6 +776,32 @@ interface AddonManifest {
|
|
|
776
776
|
audio?: boolean;
|
|
777
777
|
/** Notification permission */
|
|
778
778
|
notifications?: boolean;
|
|
779
|
+
/**
|
|
780
|
+
* File access permission for uploaded media files.
|
|
781
|
+
* When enabled, the addon can receive uploaded files (video, audio, images)
|
|
782
|
+
* via secure blob transfer for playback.
|
|
783
|
+
*
|
|
784
|
+
* This is required for addons that play user-uploaded media content.
|
|
785
|
+
*/
|
|
786
|
+
fileAccess?: {
|
|
787
|
+
/** User-friendly reason why this permission is needed */
|
|
788
|
+
reason: string;
|
|
789
|
+
};
|
|
790
|
+
/**
|
|
791
|
+
* Same-origin permission for blob URL support.
|
|
792
|
+
* Required for addons that handle large local files (videos, audio).
|
|
793
|
+
*
|
|
794
|
+
* ⚠️ SECURITY WARNING: This allows the addon to access:
|
|
795
|
+
* - localStorage/sessionStorage of the parent origin
|
|
796
|
+
* - Cookies of the parent domain
|
|
797
|
+
* - IndexedDB of the parent origin
|
|
798
|
+
*
|
|
799
|
+
* Only grant to trusted addons!
|
|
800
|
+
*/
|
|
801
|
+
sameOrigin?: {
|
|
802
|
+
/** User-friendly reason why this permission is needed */
|
|
803
|
+
reason: string;
|
|
804
|
+
};
|
|
779
805
|
};
|
|
780
806
|
/**
|
|
781
807
|
* Default layout for automatic positioning.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @mywallpaper/addon-sdk - TypeScript Types v2.
|
|
2
|
+
* @mywallpaper/addon-sdk - TypeScript Types v2.4
|
|
3
3
|
*
|
|
4
4
|
* Complete type definitions for building MyWallpaper addons.
|
|
5
5
|
* These types describe the runtime API available via window.MyWallpaper
|
|
@@ -72,7 +72,7 @@ type OAuthPermissionType = 'oauth:github' | 'oauth:google' | 'oauth:discord' | '
|
|
|
72
72
|
* Permission types that addons can request.
|
|
73
73
|
* Requested via `window.MyWallpaper.requestPermission(type, reason)`
|
|
74
74
|
*/
|
|
75
|
-
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | OAuthPermissionType;
|
|
75
|
+
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | 'same-origin' | OAuthPermissionType;
|
|
76
76
|
/**
|
|
77
77
|
* Constraints that may be applied to granted permissions.
|
|
78
78
|
* Returned when permission is granted with limitations.
|
|
@@ -418,7 +418,7 @@ interface MyWallpaperAPI {
|
|
|
418
418
|
/**
|
|
419
419
|
* SDK version.
|
|
420
420
|
*/
|
|
421
|
-
readonly version: '2.
|
|
421
|
+
readonly version: '2.4.0';
|
|
422
422
|
/**
|
|
423
423
|
* User language (detected by host).
|
|
424
424
|
*/
|
|
@@ -607,7 +607,7 @@ type AddonValues = Record<string, unknown>;
|
|
|
607
607
|
/**
|
|
608
608
|
* Available setting types for addon configuration.
|
|
609
609
|
*/
|
|
610
|
-
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
610
|
+
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'video' | 'audio' | 'file' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
611
611
|
/**
|
|
612
612
|
* Definition for a single addon setting.
|
|
613
613
|
*
|
|
@@ -776,6 +776,32 @@ interface AddonManifest {
|
|
|
776
776
|
audio?: boolean;
|
|
777
777
|
/** Notification permission */
|
|
778
778
|
notifications?: boolean;
|
|
779
|
+
/**
|
|
780
|
+
* File access permission for uploaded media files.
|
|
781
|
+
* When enabled, the addon can receive uploaded files (video, audio, images)
|
|
782
|
+
* via secure blob transfer for playback.
|
|
783
|
+
*
|
|
784
|
+
* This is required for addons that play user-uploaded media content.
|
|
785
|
+
*/
|
|
786
|
+
fileAccess?: {
|
|
787
|
+
/** User-friendly reason why this permission is needed */
|
|
788
|
+
reason: string;
|
|
789
|
+
};
|
|
790
|
+
/**
|
|
791
|
+
* Same-origin permission for blob URL support.
|
|
792
|
+
* Required for addons that handle large local files (videos, audio).
|
|
793
|
+
*
|
|
794
|
+
* ⚠️ SECURITY WARNING: This allows the addon to access:
|
|
795
|
+
* - localStorage/sessionStorage of the parent origin
|
|
796
|
+
* - Cookies of the parent domain
|
|
797
|
+
* - IndexedDB of the parent origin
|
|
798
|
+
*
|
|
799
|
+
* Only grant to trusted addons!
|
|
800
|
+
*/
|
|
801
|
+
sameOrigin?: {
|
|
802
|
+
/** User-friendly reason why this permission is needed */
|
|
803
|
+
reason: string;
|
|
804
|
+
};
|
|
779
805
|
};
|
|
780
806
|
/**
|
|
781
807
|
* Default layout for automatic positioning.
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manifest.ts","../src/utils.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n","/**\n * @mywallpaper/addon-sdk - Utility Functions\n *\n * Helper utilities for addon development.\n */\n\nimport type { AddonManifest, SettingDefinition } from './manifest'\n\n// =============================================================================\n// CONFIG UTILITIES\n// =============================================================================\n\n/**\n * Creates a default configuration object from manifest settings.\n *\n * @param manifest - Addon manifest with settings definitions\n * @returns Object with all setting keys and their default values\n *\n * @example\n * ```typescript\n * const manifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ff0000' },\n * speed: { type: 'range', default: 1, min: 0, max: 10 }\n * }\n * }\n *\n * const defaults = createDefaultConfig(manifest)\n * // { color: '#ff0000', speed: 1 }\n * ```\n */\nexport function createDefaultConfig(manifest: AddonManifest): Record<string, unknown> {\n const config: Record<string, unknown> = {}\n\n if (!manifest.settings) return config\n\n for (const [key, setting] of Object.entries(manifest.settings)) {\n if (setting.type === 'section' || setting.type === 'button') {\n // Skip non-value settings\n continue\n }\n config[key] = setting.default ?? getTypeDefault(setting)\n }\n\n return config\n}\n\n/**\n * Get type-appropriate default value when none specified.\n */\nfunction getTypeDefault(setting: SettingDefinition): unknown {\n switch (setting.type) {\n case 'string':\n case 'textarea':\n return ''\n case 'number':\n return 0\n case 'boolean':\n return false\n case 'color':\n return '#000000'\n case 'select':\n case 'radio':\n return setting.options?.[0]?.value ?? ''\n case 'range':\n return setting.min ?? 0\n case 'image':\n return null\n case 'gradient':\n return { stops: [{ color: '#000000', position: 0 }, { color: '#ffffff', position: 100 }] }\n case 'vector2':\n return { x: 0, y: 0 }\n case 'multi-select':\n return []\n default:\n return null\n }\n}\n\n/**\n * Merges user config with defaults, ensuring all keys exist.\n *\n * @param defaults - Default configuration\n * @param userConfig - User-provided configuration (may be partial)\n * @returns Complete configuration with user values taking precedence\n *\n * @example\n * ```typescript\n * const defaults = { color: '#ff0000', speed: 1 }\n * const user = { color: '#00ff00' }\n *\n * const config = mergeConfigs(defaults, user)\n * // { color: '#00ff00', speed: 1 }\n * ```\n */\nexport function mergeConfigs(\n defaults: Record<string, unknown>,\n userConfig: Record<string, unknown>\n): Record<string, unknown> {\n return { ...defaults, ...userConfig }\n}\n\n// =============================================================================\n// VERSION UTILITIES\n// =============================================================================\n\n/**\n * Semantic version comparison result.\n */\nexport type VersionComparison = -1 | 0 | 1\n\n/**\n * Compare two semantic versions.\n *\n * @param a - First version string\n * @param b - Second version string\n * @returns -1 if a < b, 0 if a === b, 1 if a > b\n *\n * @example\n * ```typescript\n * compareVersions('1.0.0', '1.0.1') // -1\n * compareVersions('2.0.0', '1.9.9') // 1\n * compareVersions('1.0.0', '1.0.0') // 0\n * ```\n */\nexport function compareVersions(a: string, b: string): VersionComparison {\n const parseVersion = (v: string): number[] => {\n return v.split('.').map(n => parseInt(n, 10) || 0)\n }\n\n const partsA = parseVersion(a)\n const partsB = parseVersion(b)\n\n for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {\n const numA = partsA[i] ?? 0\n const numB = partsB[i] ?? 0\n\n if (numA < numB) return -1\n if (numA > numB) return 1\n }\n\n return 0\n}\n\n/**\n * Check if version satisfies a minimum requirement.\n *\n * @param version - Version to check\n * @param minimum - Minimum required version\n * @returns True if version >= minimum\n */\nexport function satisfiesMinVersion(version: string, minimum: string): boolean {\n return compareVersions(version, minimum) >= 0\n}\n\n// =============================================================================\n// STORAGE UTILITIES\n// =============================================================================\n\n/**\n * Calculate the byte size of a value when JSON serialized.\n * Useful for quota tracking.\n *\n * @param value - Value to measure\n * @returns Size in bytes\n */\nexport function getStorageSize(value: unknown): number {\n try {\n return new Blob([JSON.stringify(value)]).size\n } catch {\n return 0\n }\n}\n\n/**\n * Format bytes as human-readable string.\n *\n * @param bytes - Number of bytes\n * @returns Formatted string (e.g., \"1.5 KB\", \"2.3 MB\")\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B'\n\n const units = ['B', 'KB', 'MB', 'GB']\n const k = 1024\n const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k))\n\n return `${(bytes / Math.pow(k, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`\n}\n\n// =============================================================================\n// TYPE GUARDS\n// =============================================================================\n\n/**\n * Check if a value is a plain object.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Check if a value is a valid color hex string.\n */\nexport function isValidColor(value: unknown): value is string {\n if (typeof value !== 'string') return false\n return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value)\n}\n\n// =============================================================================\n// DEBOUNCE/THROTTLE\n// =============================================================================\n\n/**\n * Debounce a function - only call after delay with no new calls.\n *\n * @param fn - Function to debounce\n * @param delayMs - Delay in milliseconds\n * @returns Debounced function\n *\n * @example\n * ```typescript\n * const save = debounce((data) => {\n * localStorage.setItem('data', JSON.stringify(data))\n * }, 1000)\n *\n * // Rapid calls only trigger one save after 1s\n * save({ a: 1 })\n * save({ a: 2 })\n * save({ a: 3 }) // Only this one runs (after 1s)\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delayMs: number\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n\n return (...args: Parameters<T>) => {\n if (timeoutId) clearTimeout(timeoutId)\n timeoutId = setTimeout(() => fn(...args), delayMs)\n }\n}\n\n/**\n * Throttle a function - call at most once per interval.\n *\n * @param fn - Function to throttle\n * @param intervalMs - Minimum interval between calls\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n intervalMs: number\n): (...args: Parameters<T>) => void {\n let lastCall = 0\n\n return (...args: Parameters<T>) => {\n const now = Date.now()\n if (now - lastCall >= intervalMs) {\n lastCall = now\n fn(...args)\n }\n }\n}\n\n// =============================================================================\n// EVENT HELPERS\n// =============================================================================\n\n/**\n * Create a one-time event listener that auto-removes after first call.\n *\n * @param element - DOM element or window\n * @param event - Event name\n * @param handler - Event handler\n */\nexport function once(\n element: EventTarget,\n event: string,\n handler: EventListener\n): void {\n const wrappedHandler: EventListener = (e) => {\n element.removeEventListener(event, wrappedHandler)\n handler(e)\n }\n element.addEventListener(event, wrappedHandler)\n}\n","/**\n * @mywallpaper/addon-sdk\n *\n * Official SDK for building MyWallpaper addons.\n *\n * @packageDocumentation\n *\n * ## Installation\n *\n * ```bash\n * npm install @mywallpaper/addon-sdk\n * ```\n *\n * ## Quick Start\n *\n * ```typescript\n * // Your addon's main file\n * import type { MyWallpaperAPI } from '@mywallpaper/addon-sdk'\n *\n * // The API is automatically available on window.MyWallpaper\n * const api = window.MyWallpaper\n *\n * // Access configuration\n * const { backgroundColor, animationSpeed } = api.config\n *\n * // React to setting changes (hot-reload)\n * api.onSettingsChange((settings, changedKeys) => {\n * if (changedKeys.includes('backgroundColor')) {\n * document.body.style.background = settings.backgroundColor as string\n * }\n * })\n *\n * // Subscribe to system events\n * api.onEvent('viewport:resize', ({ width, height }) => {\n * canvas.width = width\n * canvas.height = height\n * })\n *\n * // Signal addon is ready\n * api.ready({\n * capabilities: ['hot-reload'],\n * subscribedEvents: ['viewport:resize']\n * })\n *\n * // After visual rendering is complete\n * await loadImages()\n * render()\n * api.renderComplete()\n * ```\n *\n * ## Manifest Validation\n *\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ffffff' }\n * }\n * }\n *\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error(result.errors)\n * }\n * ```\n *\n * @module\n */\n\n// =============================================================================\n// TYPE EXPORTS\n// =============================================================================\n\nexport type {\n // Main API\n MyWallpaperAPI,\n StorageAPI,\n NetworkAPI,\n NetworkResponse,\n\n // OAuth API\n OAuthAPI,\n OAuthResponse,\n OAuthScopesError,\n OAuthProvider,\n OAuthPermissionType,\n\n // Events\n SystemEventType,\n SystemEventData,\n\n // Capabilities & Permissions\n AddonCapability,\n PermissionType,\n PermissionConstraints,\n\n // Callbacks\n SettingsCallback,\n EventCallback,\n ReadyOptions,\n\n // Lifecycle\n LifecycleEvent,\n\n // Utility types\n AddonValues,\n} from './types'\n\n// =============================================================================\n// MANIFEST EXPORTS\n// =============================================================================\n\nexport type {\n // Manifest types\n AddonManifest,\n SettingDefinition,\n SettingType,\n AddonDefaultLayout,\n ValidationResult,\n} from './manifest'\n\nexport {\n // Validation\n validateManifest,\n isValidManifest,\n\n // Manifest utilities\n supportsHotReload,\n getSubscribedEvents,\n getRequiredPermissions,\n} from './manifest'\n\n// =============================================================================\n// UTILITY EXPORTS\n// =============================================================================\n\nexport {\n // Config utilities\n createDefaultConfig,\n mergeConfigs,\n\n // Version utilities\n compareVersions,\n satisfiesMinVersion,\n\n // Storage utilities\n getStorageSize,\n formatBytes,\n\n // Type guards\n isPlainObject,\n isValidColor,\n\n // Function utilities\n debounce,\n throttle,\n once,\n} from './utils'\n\nexport type { VersionComparison } from './utils'\n\n// =============================================================================\n// SDK VERSION\n// =============================================================================\n\n/**\n * Current SDK version.\n */\nexport const SDK_VERSION = '2.3.0'\n\n/**\n * Minimum compatible host version.\n */\nexport const MIN_HOST_VERSION = '2.0.0'\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/manifest.ts","../src/utils.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'video' // Video upload\n | 'audio' // Audio upload (mp3, wav, ogg, etc.)\n | 'file' // Generic file upload (images + videos + audio)\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n /**\n * File access permission for uploaded media files.\n * When enabled, the addon can receive uploaded files (video, audio, images)\n * via secure blob transfer for playback.\n *\n * This is required for addons that play user-uploaded media content.\n */\n fileAccess?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n /**\n * Same-origin permission for blob URL support.\n * Required for addons that handle large local files (videos, audio).\n *\n * ⚠️ SECURITY WARNING: This allows the addon to access:\n * - localStorage/sessionStorage of the parent origin\n * - Cookies of the parent domain\n * - IndexedDB of the parent origin\n *\n * Only grant to trusted addons!\n */\n sameOrigin?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'video', 'audio', 'file', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n","/**\n * @mywallpaper/addon-sdk - Utility Functions\n *\n * Helper utilities for addon development.\n */\n\nimport type { AddonManifest, SettingDefinition } from './manifest'\n\n// =============================================================================\n// CONFIG UTILITIES\n// =============================================================================\n\n/**\n * Creates a default configuration object from manifest settings.\n *\n * @param manifest - Addon manifest with settings definitions\n * @returns Object with all setting keys and their default values\n *\n * @example\n * ```typescript\n * const manifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ff0000' },\n * speed: { type: 'range', default: 1, min: 0, max: 10 }\n * }\n * }\n *\n * const defaults = createDefaultConfig(manifest)\n * // { color: '#ff0000', speed: 1 }\n * ```\n */\nexport function createDefaultConfig(manifest: AddonManifest): Record<string, unknown> {\n const config: Record<string, unknown> = {}\n\n if (!manifest.settings) return config\n\n for (const [key, setting] of Object.entries(manifest.settings)) {\n if (setting.type === 'section' || setting.type === 'button') {\n // Skip non-value settings\n continue\n }\n config[key] = setting.default ?? getTypeDefault(setting)\n }\n\n return config\n}\n\n/**\n * Get type-appropriate default value when none specified.\n */\nfunction getTypeDefault(setting: SettingDefinition): unknown {\n switch (setting.type) {\n case 'string':\n case 'textarea':\n return ''\n case 'number':\n return 0\n case 'boolean':\n return false\n case 'color':\n return '#000000'\n case 'select':\n case 'radio':\n return setting.options?.[0]?.value ?? ''\n case 'range':\n return setting.min ?? 0\n case 'image':\n return null\n case 'gradient':\n return { stops: [{ color: '#000000', position: 0 }, { color: '#ffffff', position: 100 }] }\n case 'vector2':\n return { x: 0, y: 0 }\n case 'multi-select':\n return []\n default:\n return null\n }\n}\n\n/**\n * Merges user config with defaults, ensuring all keys exist.\n *\n * @param defaults - Default configuration\n * @param userConfig - User-provided configuration (may be partial)\n * @returns Complete configuration with user values taking precedence\n *\n * @example\n * ```typescript\n * const defaults = { color: '#ff0000', speed: 1 }\n * const user = { color: '#00ff00' }\n *\n * const config = mergeConfigs(defaults, user)\n * // { color: '#00ff00', speed: 1 }\n * ```\n */\nexport function mergeConfigs(\n defaults: Record<string, unknown>,\n userConfig: Record<string, unknown>\n): Record<string, unknown> {\n return { ...defaults, ...userConfig }\n}\n\n// =============================================================================\n// VERSION UTILITIES\n// =============================================================================\n\n/**\n * Semantic version comparison result.\n */\nexport type VersionComparison = -1 | 0 | 1\n\n/**\n * Compare two semantic versions.\n *\n * @param a - First version string\n * @param b - Second version string\n * @returns -1 if a < b, 0 if a === b, 1 if a > b\n *\n * @example\n * ```typescript\n * compareVersions('1.0.0', '1.0.1') // -1\n * compareVersions('2.0.0', '1.9.9') // 1\n * compareVersions('1.0.0', '1.0.0') // 0\n * ```\n */\nexport function compareVersions(a: string, b: string): VersionComparison {\n const parseVersion = (v: string): number[] => {\n return v.split('.').map(n => parseInt(n, 10) || 0)\n }\n\n const partsA = parseVersion(a)\n const partsB = parseVersion(b)\n\n for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {\n const numA = partsA[i] ?? 0\n const numB = partsB[i] ?? 0\n\n if (numA < numB) return -1\n if (numA > numB) return 1\n }\n\n return 0\n}\n\n/**\n * Check if version satisfies a minimum requirement.\n *\n * @param version - Version to check\n * @param minimum - Minimum required version\n * @returns True if version >= minimum\n */\nexport function satisfiesMinVersion(version: string, minimum: string): boolean {\n return compareVersions(version, minimum) >= 0\n}\n\n// =============================================================================\n// STORAGE UTILITIES\n// =============================================================================\n\n/**\n * Calculate the byte size of a value when JSON serialized.\n * Useful for quota tracking.\n *\n * @param value - Value to measure\n * @returns Size in bytes\n */\nexport function getStorageSize(value: unknown): number {\n try {\n return new Blob([JSON.stringify(value)]).size\n } catch {\n return 0\n }\n}\n\n/**\n * Format bytes as human-readable string.\n *\n * @param bytes - Number of bytes\n * @returns Formatted string (e.g., \"1.5 KB\", \"2.3 MB\")\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B'\n\n const units = ['B', 'KB', 'MB', 'GB']\n const k = 1024\n const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k))\n\n return `${(bytes / Math.pow(k, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`\n}\n\n// =============================================================================\n// TYPE GUARDS\n// =============================================================================\n\n/**\n * Check if a value is a plain object.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Check if a value is a valid color hex string.\n */\nexport function isValidColor(value: unknown): value is string {\n if (typeof value !== 'string') return false\n return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value)\n}\n\n// =============================================================================\n// DEBOUNCE/THROTTLE\n// =============================================================================\n\n/**\n * Debounce a function - only call after delay with no new calls.\n *\n * @param fn - Function to debounce\n * @param delayMs - Delay in milliseconds\n * @returns Debounced function\n *\n * @example\n * ```typescript\n * const save = debounce((data) => {\n * localStorage.setItem('data', JSON.stringify(data))\n * }, 1000)\n *\n * // Rapid calls only trigger one save after 1s\n * save({ a: 1 })\n * save({ a: 2 })\n * save({ a: 3 }) // Only this one runs (after 1s)\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delayMs: number\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n\n return (...args: Parameters<T>) => {\n if (timeoutId) clearTimeout(timeoutId)\n timeoutId = setTimeout(() => fn(...args), delayMs)\n }\n}\n\n/**\n * Throttle a function - call at most once per interval.\n *\n * @param fn - Function to throttle\n * @param intervalMs - Minimum interval between calls\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n intervalMs: number\n): (...args: Parameters<T>) => void {\n let lastCall = 0\n\n return (...args: Parameters<T>) => {\n const now = Date.now()\n if (now - lastCall >= intervalMs) {\n lastCall = now\n fn(...args)\n }\n }\n}\n\n// =============================================================================\n// EVENT HELPERS\n// =============================================================================\n\n/**\n * Create a one-time event listener that auto-removes after first call.\n *\n * @param element - DOM element or window\n * @param event - Event name\n * @param handler - Event handler\n */\nexport function once(\n element: EventTarget,\n event: string,\n handler: EventListener\n): void {\n const wrappedHandler: EventListener = (e) => {\n element.removeEventListener(event, wrappedHandler)\n handler(e)\n }\n element.addEventListener(event, wrappedHandler)\n}\n","/**\n * @mywallpaper/addon-sdk\n *\n * Official SDK for building MyWallpaper addons.\n *\n * @packageDocumentation\n *\n * ## Installation\n *\n * ```bash\n * npm install @mywallpaper/addon-sdk\n * ```\n *\n * ## Quick Start\n *\n * ```typescript\n * // Your addon's main file\n * import type { MyWallpaperAPI } from '@mywallpaper/addon-sdk'\n *\n * // The API is automatically available on window.MyWallpaper\n * const api = window.MyWallpaper\n *\n * // Access configuration\n * const { backgroundColor, animationSpeed } = api.config\n *\n * // React to setting changes (hot-reload)\n * api.onSettingsChange((settings, changedKeys) => {\n * if (changedKeys.includes('backgroundColor')) {\n * document.body.style.background = settings.backgroundColor as string\n * }\n * })\n *\n * // Subscribe to system events\n * api.onEvent('viewport:resize', ({ width, height }) => {\n * canvas.width = width\n * canvas.height = height\n * })\n *\n * // Signal addon is ready\n * api.ready({\n * capabilities: ['hot-reload'],\n * subscribedEvents: ['viewport:resize']\n * })\n *\n * // After visual rendering is complete\n * await loadImages()\n * render()\n * api.renderComplete()\n * ```\n *\n * ## Manifest Validation\n *\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ffffff' }\n * }\n * }\n *\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error(result.errors)\n * }\n * ```\n *\n * @module\n */\n\n// =============================================================================\n// TYPE EXPORTS\n// =============================================================================\n\nexport type {\n // Main API\n MyWallpaperAPI,\n StorageAPI,\n NetworkAPI,\n NetworkResponse,\n\n // OAuth API\n OAuthAPI,\n OAuthResponse,\n OAuthScopesError,\n OAuthProvider,\n OAuthPermissionType,\n\n // Events\n SystemEventType,\n SystemEventData,\n\n // Capabilities & Permissions\n AddonCapability,\n PermissionType,\n PermissionConstraints,\n\n // Callbacks\n SettingsCallback,\n EventCallback,\n ReadyOptions,\n\n // Lifecycle\n LifecycleEvent,\n\n // Utility types\n AddonValues,\n} from './types'\n\n// =============================================================================\n// MANIFEST EXPORTS\n// =============================================================================\n\nexport type {\n // Manifest types\n AddonManifest,\n SettingDefinition,\n SettingType,\n AddonDefaultLayout,\n ValidationResult,\n} from './manifest'\n\nexport {\n // Validation\n validateManifest,\n isValidManifest,\n\n // Manifest utilities\n supportsHotReload,\n getSubscribedEvents,\n getRequiredPermissions,\n} from './manifest'\n\n// =============================================================================\n// UTILITY EXPORTS\n// =============================================================================\n\nexport {\n // Config utilities\n createDefaultConfig,\n mergeConfigs,\n\n // Version utilities\n compareVersions,\n satisfiesMinVersion,\n\n // Storage utilities\n getStorageSize,\n formatBytes,\n\n // Type guards\n isPlainObject,\n isValidColor,\n\n // Function utilities\n debounce,\n throttle,\n once,\n} from './utils'\n\nexport type { VersionComparison } from './utils'\n\n// =============================================================================\n// SDK VERSION\n// =============================================================================\n\n/**\n * Current SDK version.\n */\nexport const SDK_VERSION = '2.3.0'\n\n/**\n * Minimum compatible host version.\n */\nexport const MIN_HOST_VERSION = '2.0.0'\n"]}
|
package/dist/index.mjs
CHANGED
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manifest.ts","../src/utils.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n","/**\n * @mywallpaper/addon-sdk - Utility Functions\n *\n * Helper utilities for addon development.\n */\n\nimport type { AddonManifest, SettingDefinition } from './manifest'\n\n// =============================================================================\n// CONFIG UTILITIES\n// =============================================================================\n\n/**\n * Creates a default configuration object from manifest settings.\n *\n * @param manifest - Addon manifest with settings definitions\n * @returns Object with all setting keys and their default values\n *\n * @example\n * ```typescript\n * const manifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ff0000' },\n * speed: { type: 'range', default: 1, min: 0, max: 10 }\n * }\n * }\n *\n * const defaults = createDefaultConfig(manifest)\n * // { color: '#ff0000', speed: 1 }\n * ```\n */\nexport function createDefaultConfig(manifest: AddonManifest): Record<string, unknown> {\n const config: Record<string, unknown> = {}\n\n if (!manifest.settings) return config\n\n for (const [key, setting] of Object.entries(manifest.settings)) {\n if (setting.type === 'section' || setting.type === 'button') {\n // Skip non-value settings\n continue\n }\n config[key] = setting.default ?? getTypeDefault(setting)\n }\n\n return config\n}\n\n/**\n * Get type-appropriate default value when none specified.\n */\nfunction getTypeDefault(setting: SettingDefinition): unknown {\n switch (setting.type) {\n case 'string':\n case 'textarea':\n return ''\n case 'number':\n return 0\n case 'boolean':\n return false\n case 'color':\n return '#000000'\n case 'select':\n case 'radio':\n return setting.options?.[0]?.value ?? ''\n case 'range':\n return setting.min ?? 0\n case 'image':\n return null\n case 'gradient':\n return { stops: [{ color: '#000000', position: 0 }, { color: '#ffffff', position: 100 }] }\n case 'vector2':\n return { x: 0, y: 0 }\n case 'multi-select':\n return []\n default:\n return null\n }\n}\n\n/**\n * Merges user config with defaults, ensuring all keys exist.\n *\n * @param defaults - Default configuration\n * @param userConfig - User-provided configuration (may be partial)\n * @returns Complete configuration with user values taking precedence\n *\n * @example\n * ```typescript\n * const defaults = { color: '#ff0000', speed: 1 }\n * const user = { color: '#00ff00' }\n *\n * const config = mergeConfigs(defaults, user)\n * // { color: '#00ff00', speed: 1 }\n * ```\n */\nexport function mergeConfigs(\n defaults: Record<string, unknown>,\n userConfig: Record<string, unknown>\n): Record<string, unknown> {\n return { ...defaults, ...userConfig }\n}\n\n// =============================================================================\n// VERSION UTILITIES\n// =============================================================================\n\n/**\n * Semantic version comparison result.\n */\nexport type VersionComparison = -1 | 0 | 1\n\n/**\n * Compare two semantic versions.\n *\n * @param a - First version string\n * @param b - Second version string\n * @returns -1 if a < b, 0 if a === b, 1 if a > b\n *\n * @example\n * ```typescript\n * compareVersions('1.0.0', '1.0.1') // -1\n * compareVersions('2.0.0', '1.9.9') // 1\n * compareVersions('1.0.0', '1.0.0') // 0\n * ```\n */\nexport function compareVersions(a: string, b: string): VersionComparison {\n const parseVersion = (v: string): number[] => {\n return v.split('.').map(n => parseInt(n, 10) || 0)\n }\n\n const partsA = parseVersion(a)\n const partsB = parseVersion(b)\n\n for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {\n const numA = partsA[i] ?? 0\n const numB = partsB[i] ?? 0\n\n if (numA < numB) return -1\n if (numA > numB) return 1\n }\n\n return 0\n}\n\n/**\n * Check if version satisfies a minimum requirement.\n *\n * @param version - Version to check\n * @param minimum - Minimum required version\n * @returns True if version >= minimum\n */\nexport function satisfiesMinVersion(version: string, minimum: string): boolean {\n return compareVersions(version, minimum) >= 0\n}\n\n// =============================================================================\n// STORAGE UTILITIES\n// =============================================================================\n\n/**\n * Calculate the byte size of a value when JSON serialized.\n * Useful for quota tracking.\n *\n * @param value - Value to measure\n * @returns Size in bytes\n */\nexport function getStorageSize(value: unknown): number {\n try {\n return new Blob([JSON.stringify(value)]).size\n } catch {\n return 0\n }\n}\n\n/**\n * Format bytes as human-readable string.\n *\n * @param bytes - Number of bytes\n * @returns Formatted string (e.g., \"1.5 KB\", \"2.3 MB\")\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B'\n\n const units = ['B', 'KB', 'MB', 'GB']\n const k = 1024\n const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k))\n\n return `${(bytes / Math.pow(k, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`\n}\n\n// =============================================================================\n// TYPE GUARDS\n// =============================================================================\n\n/**\n * Check if a value is a plain object.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Check if a value is a valid color hex string.\n */\nexport function isValidColor(value: unknown): value is string {\n if (typeof value !== 'string') return false\n return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value)\n}\n\n// =============================================================================\n// DEBOUNCE/THROTTLE\n// =============================================================================\n\n/**\n * Debounce a function - only call after delay with no new calls.\n *\n * @param fn - Function to debounce\n * @param delayMs - Delay in milliseconds\n * @returns Debounced function\n *\n * @example\n * ```typescript\n * const save = debounce((data) => {\n * localStorage.setItem('data', JSON.stringify(data))\n * }, 1000)\n *\n * // Rapid calls only trigger one save after 1s\n * save({ a: 1 })\n * save({ a: 2 })\n * save({ a: 3 }) // Only this one runs (after 1s)\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delayMs: number\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n\n return (...args: Parameters<T>) => {\n if (timeoutId) clearTimeout(timeoutId)\n timeoutId = setTimeout(() => fn(...args), delayMs)\n }\n}\n\n/**\n * Throttle a function - call at most once per interval.\n *\n * @param fn - Function to throttle\n * @param intervalMs - Minimum interval between calls\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n intervalMs: number\n): (...args: Parameters<T>) => void {\n let lastCall = 0\n\n return (...args: Parameters<T>) => {\n const now = Date.now()\n if (now - lastCall >= intervalMs) {\n lastCall = now\n fn(...args)\n }\n }\n}\n\n// =============================================================================\n// EVENT HELPERS\n// =============================================================================\n\n/**\n * Create a one-time event listener that auto-removes after first call.\n *\n * @param element - DOM element or window\n * @param event - Event name\n * @param handler - Event handler\n */\nexport function once(\n element: EventTarget,\n event: string,\n handler: EventListener\n): void {\n const wrappedHandler: EventListener = (e) => {\n element.removeEventListener(event, wrappedHandler)\n handler(e)\n }\n element.addEventListener(event, wrappedHandler)\n}\n","/**\n * @mywallpaper/addon-sdk\n *\n * Official SDK for building MyWallpaper addons.\n *\n * @packageDocumentation\n *\n * ## Installation\n *\n * ```bash\n * npm install @mywallpaper/addon-sdk\n * ```\n *\n * ## Quick Start\n *\n * ```typescript\n * // Your addon's main file\n * import type { MyWallpaperAPI } from '@mywallpaper/addon-sdk'\n *\n * // The API is automatically available on window.MyWallpaper\n * const api = window.MyWallpaper\n *\n * // Access configuration\n * const { backgroundColor, animationSpeed } = api.config\n *\n * // React to setting changes (hot-reload)\n * api.onSettingsChange((settings, changedKeys) => {\n * if (changedKeys.includes('backgroundColor')) {\n * document.body.style.background = settings.backgroundColor as string\n * }\n * })\n *\n * // Subscribe to system events\n * api.onEvent('viewport:resize', ({ width, height }) => {\n * canvas.width = width\n * canvas.height = height\n * })\n *\n * // Signal addon is ready\n * api.ready({\n * capabilities: ['hot-reload'],\n * subscribedEvents: ['viewport:resize']\n * })\n *\n * // After visual rendering is complete\n * await loadImages()\n * render()\n * api.renderComplete()\n * ```\n *\n * ## Manifest Validation\n *\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ffffff' }\n * }\n * }\n *\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error(result.errors)\n * }\n * ```\n *\n * @module\n */\n\n// =============================================================================\n// TYPE EXPORTS\n// =============================================================================\n\nexport type {\n // Main API\n MyWallpaperAPI,\n StorageAPI,\n NetworkAPI,\n NetworkResponse,\n\n // OAuth API\n OAuthAPI,\n OAuthResponse,\n OAuthScopesError,\n OAuthProvider,\n OAuthPermissionType,\n\n // Events\n SystemEventType,\n SystemEventData,\n\n // Capabilities & Permissions\n AddonCapability,\n PermissionType,\n PermissionConstraints,\n\n // Callbacks\n SettingsCallback,\n EventCallback,\n ReadyOptions,\n\n // Lifecycle\n LifecycleEvent,\n\n // Utility types\n AddonValues,\n} from './types'\n\n// =============================================================================\n// MANIFEST EXPORTS\n// =============================================================================\n\nexport type {\n // Manifest types\n AddonManifest,\n SettingDefinition,\n SettingType,\n AddonDefaultLayout,\n ValidationResult,\n} from './manifest'\n\nexport {\n // Validation\n validateManifest,\n isValidManifest,\n\n // Manifest utilities\n supportsHotReload,\n getSubscribedEvents,\n getRequiredPermissions,\n} from './manifest'\n\n// =============================================================================\n// UTILITY EXPORTS\n// =============================================================================\n\nexport {\n // Config utilities\n createDefaultConfig,\n mergeConfigs,\n\n // Version utilities\n compareVersions,\n satisfiesMinVersion,\n\n // Storage utilities\n getStorageSize,\n formatBytes,\n\n // Type guards\n isPlainObject,\n isValidColor,\n\n // Function utilities\n debounce,\n throttle,\n once,\n} from './utils'\n\nexport type { VersionComparison } from './utils'\n\n// =============================================================================\n// SDK VERSION\n// =============================================================================\n\n/**\n * Current SDK version.\n */\nexport const SDK_VERSION = '2.3.0'\n\n/**\n * Minimum compatible host version.\n */\nexport const MIN_HOST_VERSION = '2.0.0'\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/manifest.ts","../src/utils.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'video' // Video upload\n | 'audio' // Audio upload (mp3, wav, ogg, etc.)\n | 'file' // Generic file upload (images + videos + audio)\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n /**\n * File access permission for uploaded media files.\n * When enabled, the addon can receive uploaded files (video, audio, images)\n * via secure blob transfer for playback.\n *\n * This is required for addons that play user-uploaded media content.\n */\n fileAccess?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n /**\n * Same-origin permission for blob URL support.\n * Required for addons that handle large local files (videos, audio).\n *\n * ⚠️ SECURITY WARNING: This allows the addon to access:\n * - localStorage/sessionStorage of the parent origin\n * - Cookies of the parent domain\n * - IndexedDB of the parent origin\n *\n * Only grant to trusted addons!\n */\n sameOrigin?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'video', 'audio', 'file', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n","/**\n * @mywallpaper/addon-sdk - Utility Functions\n *\n * Helper utilities for addon development.\n */\n\nimport type { AddonManifest, SettingDefinition } from './manifest'\n\n// =============================================================================\n// CONFIG UTILITIES\n// =============================================================================\n\n/**\n * Creates a default configuration object from manifest settings.\n *\n * @param manifest - Addon manifest with settings definitions\n * @returns Object with all setting keys and their default values\n *\n * @example\n * ```typescript\n * const manifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ff0000' },\n * speed: { type: 'range', default: 1, min: 0, max: 10 }\n * }\n * }\n *\n * const defaults = createDefaultConfig(manifest)\n * // { color: '#ff0000', speed: 1 }\n * ```\n */\nexport function createDefaultConfig(manifest: AddonManifest): Record<string, unknown> {\n const config: Record<string, unknown> = {}\n\n if (!manifest.settings) return config\n\n for (const [key, setting] of Object.entries(manifest.settings)) {\n if (setting.type === 'section' || setting.type === 'button') {\n // Skip non-value settings\n continue\n }\n config[key] = setting.default ?? getTypeDefault(setting)\n }\n\n return config\n}\n\n/**\n * Get type-appropriate default value when none specified.\n */\nfunction getTypeDefault(setting: SettingDefinition): unknown {\n switch (setting.type) {\n case 'string':\n case 'textarea':\n return ''\n case 'number':\n return 0\n case 'boolean':\n return false\n case 'color':\n return '#000000'\n case 'select':\n case 'radio':\n return setting.options?.[0]?.value ?? ''\n case 'range':\n return setting.min ?? 0\n case 'image':\n return null\n case 'gradient':\n return { stops: [{ color: '#000000', position: 0 }, { color: '#ffffff', position: 100 }] }\n case 'vector2':\n return { x: 0, y: 0 }\n case 'multi-select':\n return []\n default:\n return null\n }\n}\n\n/**\n * Merges user config with defaults, ensuring all keys exist.\n *\n * @param defaults - Default configuration\n * @param userConfig - User-provided configuration (may be partial)\n * @returns Complete configuration with user values taking precedence\n *\n * @example\n * ```typescript\n * const defaults = { color: '#ff0000', speed: 1 }\n * const user = { color: '#00ff00' }\n *\n * const config = mergeConfigs(defaults, user)\n * // { color: '#00ff00', speed: 1 }\n * ```\n */\nexport function mergeConfigs(\n defaults: Record<string, unknown>,\n userConfig: Record<string, unknown>\n): Record<string, unknown> {\n return { ...defaults, ...userConfig }\n}\n\n// =============================================================================\n// VERSION UTILITIES\n// =============================================================================\n\n/**\n * Semantic version comparison result.\n */\nexport type VersionComparison = -1 | 0 | 1\n\n/**\n * Compare two semantic versions.\n *\n * @param a - First version string\n * @param b - Second version string\n * @returns -1 if a < b, 0 if a === b, 1 if a > b\n *\n * @example\n * ```typescript\n * compareVersions('1.0.0', '1.0.1') // -1\n * compareVersions('2.0.0', '1.9.9') // 1\n * compareVersions('1.0.0', '1.0.0') // 0\n * ```\n */\nexport function compareVersions(a: string, b: string): VersionComparison {\n const parseVersion = (v: string): number[] => {\n return v.split('.').map(n => parseInt(n, 10) || 0)\n }\n\n const partsA = parseVersion(a)\n const partsB = parseVersion(b)\n\n for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {\n const numA = partsA[i] ?? 0\n const numB = partsB[i] ?? 0\n\n if (numA < numB) return -1\n if (numA > numB) return 1\n }\n\n return 0\n}\n\n/**\n * Check if version satisfies a minimum requirement.\n *\n * @param version - Version to check\n * @param minimum - Minimum required version\n * @returns True if version >= minimum\n */\nexport function satisfiesMinVersion(version: string, minimum: string): boolean {\n return compareVersions(version, minimum) >= 0\n}\n\n// =============================================================================\n// STORAGE UTILITIES\n// =============================================================================\n\n/**\n * Calculate the byte size of a value when JSON serialized.\n * Useful for quota tracking.\n *\n * @param value - Value to measure\n * @returns Size in bytes\n */\nexport function getStorageSize(value: unknown): number {\n try {\n return new Blob([JSON.stringify(value)]).size\n } catch {\n return 0\n }\n}\n\n/**\n * Format bytes as human-readable string.\n *\n * @param bytes - Number of bytes\n * @returns Formatted string (e.g., \"1.5 KB\", \"2.3 MB\")\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B'\n\n const units = ['B', 'KB', 'MB', 'GB']\n const k = 1024\n const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k))\n\n return `${(bytes / Math.pow(k, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`\n}\n\n// =============================================================================\n// TYPE GUARDS\n// =============================================================================\n\n/**\n * Check if a value is a plain object.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Check if a value is a valid color hex string.\n */\nexport function isValidColor(value: unknown): value is string {\n if (typeof value !== 'string') return false\n return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value)\n}\n\n// =============================================================================\n// DEBOUNCE/THROTTLE\n// =============================================================================\n\n/**\n * Debounce a function - only call after delay with no new calls.\n *\n * @param fn - Function to debounce\n * @param delayMs - Delay in milliseconds\n * @returns Debounced function\n *\n * @example\n * ```typescript\n * const save = debounce((data) => {\n * localStorage.setItem('data', JSON.stringify(data))\n * }, 1000)\n *\n * // Rapid calls only trigger one save after 1s\n * save({ a: 1 })\n * save({ a: 2 })\n * save({ a: 3 }) // Only this one runs (after 1s)\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delayMs: number\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n\n return (...args: Parameters<T>) => {\n if (timeoutId) clearTimeout(timeoutId)\n timeoutId = setTimeout(() => fn(...args), delayMs)\n }\n}\n\n/**\n * Throttle a function - call at most once per interval.\n *\n * @param fn - Function to throttle\n * @param intervalMs - Minimum interval between calls\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n intervalMs: number\n): (...args: Parameters<T>) => void {\n let lastCall = 0\n\n return (...args: Parameters<T>) => {\n const now = Date.now()\n if (now - lastCall >= intervalMs) {\n lastCall = now\n fn(...args)\n }\n }\n}\n\n// =============================================================================\n// EVENT HELPERS\n// =============================================================================\n\n/**\n * Create a one-time event listener that auto-removes after first call.\n *\n * @param element - DOM element or window\n * @param event - Event name\n * @param handler - Event handler\n */\nexport function once(\n element: EventTarget,\n event: string,\n handler: EventListener\n): void {\n const wrappedHandler: EventListener = (e) => {\n element.removeEventListener(event, wrappedHandler)\n handler(e)\n }\n element.addEventListener(event, wrappedHandler)\n}\n","/**\n * @mywallpaper/addon-sdk\n *\n * Official SDK for building MyWallpaper addons.\n *\n * @packageDocumentation\n *\n * ## Installation\n *\n * ```bash\n * npm install @mywallpaper/addon-sdk\n * ```\n *\n * ## Quick Start\n *\n * ```typescript\n * // Your addon's main file\n * import type { MyWallpaperAPI } from '@mywallpaper/addon-sdk'\n *\n * // The API is automatically available on window.MyWallpaper\n * const api = window.MyWallpaper\n *\n * // Access configuration\n * const { backgroundColor, animationSpeed } = api.config\n *\n * // React to setting changes (hot-reload)\n * api.onSettingsChange((settings, changedKeys) => {\n * if (changedKeys.includes('backgroundColor')) {\n * document.body.style.background = settings.backgroundColor as string\n * }\n * })\n *\n * // Subscribe to system events\n * api.onEvent('viewport:resize', ({ width, height }) => {\n * canvas.width = width\n * canvas.height = height\n * })\n *\n * // Signal addon is ready\n * api.ready({\n * capabilities: ['hot-reload'],\n * subscribedEvents: ['viewport:resize']\n * })\n *\n * // After visual rendering is complete\n * await loadImages()\n * render()\n * api.renderComplete()\n * ```\n *\n * ## Manifest Validation\n *\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * settings: {\n * color: { type: 'color', default: '#ffffff' }\n * }\n * }\n *\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error(result.errors)\n * }\n * ```\n *\n * @module\n */\n\n// =============================================================================\n// TYPE EXPORTS\n// =============================================================================\n\nexport type {\n // Main API\n MyWallpaperAPI,\n StorageAPI,\n NetworkAPI,\n NetworkResponse,\n\n // OAuth API\n OAuthAPI,\n OAuthResponse,\n OAuthScopesError,\n OAuthProvider,\n OAuthPermissionType,\n\n // Events\n SystemEventType,\n SystemEventData,\n\n // Capabilities & Permissions\n AddonCapability,\n PermissionType,\n PermissionConstraints,\n\n // Callbacks\n SettingsCallback,\n EventCallback,\n ReadyOptions,\n\n // Lifecycle\n LifecycleEvent,\n\n // Utility types\n AddonValues,\n} from './types'\n\n// =============================================================================\n// MANIFEST EXPORTS\n// =============================================================================\n\nexport type {\n // Manifest types\n AddonManifest,\n SettingDefinition,\n SettingType,\n AddonDefaultLayout,\n ValidationResult,\n} from './manifest'\n\nexport {\n // Validation\n validateManifest,\n isValidManifest,\n\n // Manifest utilities\n supportsHotReload,\n getSubscribedEvents,\n getRequiredPermissions,\n} from './manifest'\n\n// =============================================================================\n// UTILITY EXPORTS\n// =============================================================================\n\nexport {\n // Config utilities\n createDefaultConfig,\n mergeConfigs,\n\n // Version utilities\n compareVersions,\n satisfiesMinVersion,\n\n // Storage utilities\n getStorageSize,\n formatBytes,\n\n // Type guards\n isPlainObject,\n isValidColor,\n\n // Function utilities\n debounce,\n throttle,\n once,\n} from './utils'\n\nexport type { VersionComparison } from './utils'\n\n// =============================================================================\n// SDK VERSION\n// =============================================================================\n\n/**\n * Current SDK version.\n */\nexport const SDK_VERSION = '2.3.0'\n\n/**\n * Minimum compatible host version.\n */\nexport const MIN_HOST_VERSION = '2.0.0'\n"]}
|
package/dist/manifest.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @mywallpaper/addon-sdk - TypeScript Types v2.
|
|
2
|
+
* @mywallpaper/addon-sdk - TypeScript Types v2.4
|
|
3
3
|
*
|
|
4
4
|
* Complete type definitions for building MyWallpaper addons.
|
|
5
5
|
* These types describe the runtime API available via window.MyWallpaper
|
|
@@ -72,7 +72,7 @@ type OAuthPermissionType = 'oauth:github' | 'oauth:google' | 'oauth:discord' | '
|
|
|
72
72
|
* Permission types that addons can request.
|
|
73
73
|
* Requested via `window.MyWallpaper.requestPermission(type, reason)`
|
|
74
74
|
*/
|
|
75
|
-
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | OAuthPermissionType;
|
|
75
|
+
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | 'same-origin' | OAuthPermissionType;
|
|
76
76
|
/**
|
|
77
77
|
* Storage API for persistent key-value data.
|
|
78
78
|
* Access via `window.MyWallpaper.storage`
|
|
@@ -403,7 +403,7 @@ interface MyWallpaperAPI {
|
|
|
403
403
|
/**
|
|
404
404
|
* SDK version.
|
|
405
405
|
*/
|
|
406
|
-
readonly version: '2.
|
|
406
|
+
readonly version: '2.4.0';
|
|
407
407
|
/**
|
|
408
408
|
* User language (detected by host).
|
|
409
409
|
*/
|
|
@@ -584,7 +584,7 @@ declare global {
|
|
|
584
584
|
/**
|
|
585
585
|
* Available setting types for addon configuration.
|
|
586
586
|
*/
|
|
587
|
-
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
587
|
+
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'video' | 'audio' | 'file' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
588
588
|
/**
|
|
589
589
|
* Definition for a single addon setting.
|
|
590
590
|
*
|
|
@@ -753,6 +753,32 @@ interface AddonManifest {
|
|
|
753
753
|
audio?: boolean;
|
|
754
754
|
/** Notification permission */
|
|
755
755
|
notifications?: boolean;
|
|
756
|
+
/**
|
|
757
|
+
* File access permission for uploaded media files.
|
|
758
|
+
* When enabled, the addon can receive uploaded files (video, audio, images)
|
|
759
|
+
* via secure blob transfer for playback.
|
|
760
|
+
*
|
|
761
|
+
* This is required for addons that play user-uploaded media content.
|
|
762
|
+
*/
|
|
763
|
+
fileAccess?: {
|
|
764
|
+
/** User-friendly reason why this permission is needed */
|
|
765
|
+
reason: string;
|
|
766
|
+
};
|
|
767
|
+
/**
|
|
768
|
+
* Same-origin permission for blob URL support.
|
|
769
|
+
* Required for addons that handle large local files (videos, audio).
|
|
770
|
+
*
|
|
771
|
+
* ⚠️ SECURITY WARNING: This allows the addon to access:
|
|
772
|
+
* - localStorage/sessionStorage of the parent origin
|
|
773
|
+
* - Cookies of the parent domain
|
|
774
|
+
* - IndexedDB of the parent origin
|
|
775
|
+
*
|
|
776
|
+
* Only grant to trusted addons!
|
|
777
|
+
*/
|
|
778
|
+
sameOrigin?: {
|
|
779
|
+
/** User-friendly reason why this permission is needed */
|
|
780
|
+
reason: string;
|
|
781
|
+
};
|
|
756
782
|
};
|
|
757
783
|
/**
|
|
758
784
|
* Default layout for automatic positioning.
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @mywallpaper/addon-sdk - TypeScript Types v2.
|
|
2
|
+
* @mywallpaper/addon-sdk - TypeScript Types v2.4
|
|
3
3
|
*
|
|
4
4
|
* Complete type definitions for building MyWallpaper addons.
|
|
5
5
|
* These types describe the runtime API available via window.MyWallpaper
|
|
@@ -72,7 +72,7 @@ type OAuthPermissionType = 'oauth:github' | 'oauth:google' | 'oauth:discord' | '
|
|
|
72
72
|
* Permission types that addons can request.
|
|
73
73
|
* Requested via `window.MyWallpaper.requestPermission(type, reason)`
|
|
74
74
|
*/
|
|
75
|
-
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | OAuthPermissionType;
|
|
75
|
+
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | 'same-origin' | OAuthPermissionType;
|
|
76
76
|
/**
|
|
77
77
|
* Storage API for persistent key-value data.
|
|
78
78
|
* Access via `window.MyWallpaper.storage`
|
|
@@ -403,7 +403,7 @@ interface MyWallpaperAPI {
|
|
|
403
403
|
/**
|
|
404
404
|
* SDK version.
|
|
405
405
|
*/
|
|
406
|
-
readonly version: '2.
|
|
406
|
+
readonly version: '2.4.0';
|
|
407
407
|
/**
|
|
408
408
|
* User language (detected by host).
|
|
409
409
|
*/
|
|
@@ -584,7 +584,7 @@ declare global {
|
|
|
584
584
|
/**
|
|
585
585
|
* Available setting types for addon configuration.
|
|
586
586
|
*/
|
|
587
|
-
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
587
|
+
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'video' | 'audio' | 'file' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
588
588
|
/**
|
|
589
589
|
* Definition for a single addon setting.
|
|
590
590
|
*
|
|
@@ -753,6 +753,32 @@ interface AddonManifest {
|
|
|
753
753
|
audio?: boolean;
|
|
754
754
|
/** Notification permission */
|
|
755
755
|
notifications?: boolean;
|
|
756
|
+
/**
|
|
757
|
+
* File access permission for uploaded media files.
|
|
758
|
+
* When enabled, the addon can receive uploaded files (video, audio, images)
|
|
759
|
+
* via secure blob transfer for playback.
|
|
760
|
+
*
|
|
761
|
+
* This is required for addons that play user-uploaded media content.
|
|
762
|
+
*/
|
|
763
|
+
fileAccess?: {
|
|
764
|
+
/** User-friendly reason why this permission is needed */
|
|
765
|
+
reason: string;
|
|
766
|
+
};
|
|
767
|
+
/**
|
|
768
|
+
* Same-origin permission for blob URL support.
|
|
769
|
+
* Required for addons that handle large local files (videos, audio).
|
|
770
|
+
*
|
|
771
|
+
* ⚠️ SECURITY WARNING: This allows the addon to access:
|
|
772
|
+
* - localStorage/sessionStorage of the parent origin
|
|
773
|
+
* - Cookies of the parent domain
|
|
774
|
+
* - IndexedDB of the parent origin
|
|
775
|
+
*
|
|
776
|
+
* Only grant to trusted addons!
|
|
777
|
+
*/
|
|
778
|
+
sameOrigin?: {
|
|
779
|
+
/** User-friendly reason why this permission is needed */
|
|
780
|
+
reason: string;
|
|
781
|
+
};
|
|
756
782
|
};
|
|
757
783
|
/**
|
|
758
784
|
* Default layout for automatic positioning.
|
package/dist/manifest.js
CHANGED
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":"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","file":"manifest.js","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":"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","file":"manifest.js","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'video' // Video upload\n | 'audio' // Audio upload (mp3, wav, ogg, etc.)\n | 'file' // Generic file upload (images + videos + audio)\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n /**\n * File access permission for uploaded media files.\n * When enabled, the addon can receive uploaded files (video, audio, images)\n * via secure blob transfer for playback.\n *\n * This is required for addons that play user-uploaded media content.\n */\n fileAccess?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n /**\n * Same-origin permission for blob URL support.\n * Required for addons that handle large local files (videos, audio).\n *\n * ⚠️ SECURITY WARNING: This allows the addon to access:\n * - localStorage/sessionStorage of the parent origin\n * - Cookies of the parent domain\n * - IndexedDB of the parent origin\n *\n * Only grant to trusted addons!\n */\n sameOrigin?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'video', 'audio', 'file', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n"]}
|
package/dist/manifest.mjs
CHANGED
package/dist/manifest.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":"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","file":"manifest.mjs","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":"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","file":"manifest.mjs","sourcesContent":["/**\n * @mywallpaper/addon-sdk - Manifest Schema & Validation\n *\n * Defines the addon manifest format (manifest.json) and provides\n * Zod-based validation for type safety at runtime.\n *\n * @example\n * ```typescript\n * import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'\n *\n * const manifest: AddonManifest = {\n * name: 'My Addon',\n * version: '1.0.0',\n * sdkVersion: '2.3.0',\n * description: 'A cool addon',\n * settings: {\n * primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }\n * }\n * }\n *\n * // Validate at runtime\n * const result = validateManifest(manifest)\n * if (!result.success) {\n * console.error('Invalid manifest:', result.errors)\n * }\n * ```\n */\n\nimport type { SystemEventType, PermissionType, OAuthProvider } from './types'\n\n// =============================================================================\n// SETTING TYPES\n// =============================================================================\n\n/**\n * Available setting types for addon configuration.\n */\nexport type SettingType =\n | 'string' // Text input\n | 'number' // Numeric input\n | 'boolean' // Checkbox/toggle\n | 'color' // Color picker\n | 'select' // Dropdown selection\n | 'range' // Slider\n | 'image' // Image upload\n | 'video' // Video upload\n | 'audio' // Audio upload (mp3, wav, ogg, etc.)\n | 'file' // Generic file upload (images + videos + audio)\n | 'section' // Visual grouping (no value)\n | 'textarea' // Multi-line text\n | 'radio' // Radio buttons\n | 'gradient' // Gradient picker\n | 'vector2' // 2D vector (x, y)\n | 'button' // Action button (no value)\n | 'multi-select' // Multiple selection\n\n/**\n * Definition for a single addon setting.\n *\n * @example\n * ```typescript\n * const colorSetting: SettingDefinition = {\n * type: 'color',\n * label: 'Background Color',\n * description: 'Choose a background color',\n * default: '#000000'\n * }\n *\n * const speedSetting: SettingDefinition = {\n * type: 'range',\n * label: 'Animation Speed',\n * min: 0.1,\n * max: 10,\n * step: 0.1,\n * default: 1\n * }\n * ```\n */\nexport interface SettingDefinition {\n /** Setting type determines the UI control */\n type: SettingType\n\n /** Human-readable label */\n label?: string\n\n /** Help text shown below the control */\n description?: string\n\n /** Default value (type depends on setting type) */\n default?: unknown\n\n // Select/Radio/Multi-select options\n /** Options for select, radio, multi-select types */\n options?: Array<{ value: string; label: string }>\n\n // Range-specific\n /** Minimum value for range type */\n min?: number\n /** Maximum value for range type */\n max?: number\n /** Step increment for range type */\n step?: number\n\n // Button-specific\n /** Label text on button */\n buttonLabel?: string\n\n // Vector-specific\n /** Axis labels for vector types, e.g., ['X', 'Y'] */\n axisLabels?: [string, string] | [string, string, string]\n\n // Multi-select specific\n /** Maximum number of selections */\n maxItems?: number\n\n // Conditional visibility\n /** Only show if another setting has a specific value */\n showIf?: {\n setting: string\n equals: unknown\n }\n}\n\n// =============================================================================\n// MANIFEST SCHEMA\n// =============================================================================\n\n/**\n * Default layout configuration for addon positioning.\n */\nexport interface AddonDefaultLayout {\n /** X position as percentage of viewport width (0-100) */\n xPercent?: number\n /** Y position as percentage of viewport height (0-100) */\n yPercent?: number\n /** Width as percentage of viewport width (0-100) */\n widthPercent?: number\n /** Height as percentage of viewport height (0-100) */\n heightPercent?: number\n /** Rotation in degrees */\n rotation?: number\n}\n\n/**\n * The complete addon manifest schema.\n * This defines your addon's identity, settings, and capabilities.\n *\n * @example\n * ```json\n * {\n * \"name\": \"Weather Widget\",\n * \"version\": \"1.2.0\",\n * \"description\": \"Displays current weather conditions\",\n * \"author\": \"Jane Developer\",\n * \"categories\": [\"utilities\", \"weather\"],\n * \"capabilities\": {\n * \"hotReload\": true,\n * \"systemEvents\": [\"viewport:resize\", \"theme:change\"]\n * },\n * \"permissions\": {\n * \"storage\": { \"quota\": 512 },\n * \"network\": { \"domains\": [\"api.openweathermap.org\"] }\n * },\n * \"settings\": {\n * \"apiKey\": {\n * \"type\": \"string\",\n * \"label\": \"API Key\",\n * \"description\": \"Get your key at openweathermap.org\"\n * },\n * \"unit\": {\n * \"type\": \"select\",\n * \"label\": \"Temperature Unit\",\n * \"default\": \"celsius\",\n * \"options\": [\n * { \"value\": \"celsius\", \"label\": \"Celsius\" },\n * { \"value\": \"fahrenheit\", \"label\": \"Fahrenheit\" }\n * ]\n * }\n * }\n * }\n * ```\n */\nexport interface AddonManifest {\n // ---------------------------------------------------------------------------\n // REQUIRED FIELDS\n // ---------------------------------------------------------------------------\n\n /** Addon name (displayed in UI) */\n name: string\n\n /** Semantic version (e.g., \"1.0.0\") */\n version: string\n\n /**\n * SDK version this addon was built for.\n * Must be a valid semver (e.g., \"2.3.0\").\n * Host uses this to inject the correct client-side SDK.\n */\n sdkVersion: string\n\n // ---------------------------------------------------------------------------\n // OPTIONAL METADATA\n // ---------------------------------------------------------------------------\n\n /** Short description of the addon */\n description?: string\n\n /** Author name or organization */\n author?: string\n\n /** Addon type/category for filtering */\n type?: string\n\n /** Categories for discoverability */\n categories?: string[]\n\n /** License (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string\n\n /** Homepage or documentation URL */\n homepage?: string\n\n /** Repository URL */\n repository?: string\n\n // ---------------------------------------------------------------------------\n // SETTINGS\n // ---------------------------------------------------------------------------\n\n /**\n * User-configurable settings.\n * Key is the setting ID, value is the definition.\n */\n settings?: Record<string, SettingDefinition>\n\n // ---------------------------------------------------------------------------\n // CAPABILITIES\n // ---------------------------------------------------------------------------\n\n /**\n * Addon capabilities and event subscriptions.\n */\n capabilities?: {\n /** Supports settings changes without full reload */\n hotReload?: boolean\n /** System events the addon wants to receive */\n systemEvents?: SystemEventType[]\n }\n\n // ---------------------------------------------------------------------------\n // PERMISSIONS\n // ---------------------------------------------------------------------------\n\n /**\n * Permissions the addon will request.\n * Declaring them here improves UX by showing upfront.\n */\n permissions?: {\n /** Storage permission with quota in KB */\n storage?: { quota: number }\n /** CPU usage limit */\n cpu?: { limit: 'low' | 'medium' | 'high' }\n /** Network access with allowed domains */\n network?: { domains: string[] }\n /** Audio playback permission */\n audio?: boolean\n /** Notification permission */\n notifications?: boolean\n /**\n * File access permission for uploaded media files.\n * When enabled, the addon can receive uploaded files (video, audio, images)\n * via secure blob transfer for playback.\n *\n * This is required for addons that play user-uploaded media content.\n */\n fileAccess?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n /**\n * Same-origin permission for blob URL support.\n * Required for addons that handle large local files (videos, audio).\n *\n * ⚠️ SECURITY WARNING: This allows the addon to access:\n * - localStorage/sessionStorage of the parent origin\n * - Cookies of the parent domain\n * - IndexedDB of the parent origin\n *\n * Only grant to trusted addons!\n */\n sameOrigin?: {\n /** User-friendly reason why this permission is needed */\n reason: string\n }\n }\n\n // ---------------------------------------------------------------------------\n // LAYOUT\n // ---------------------------------------------------------------------------\n\n /**\n * Default layout for automatic positioning.\n * Used when addon is first added to a wallpaper.\n */\n defaultLayout?: AddonDefaultLayout\n\n // ---------------------------------------------------------------------------\n // FUTURE FIELDS\n // ---------------------------------------------------------------------------\n\n /**\n * OAuth providers and scopes for authenticated addons.\n *\n * Declare the OAuth providers your addon needs and the scopes it requires.\n * Scopes are requested incrementally - users can connect with basic scopes\n * first, and your addon can request additional scopes when needed.\n *\n * @example\n * ```json\n * {\n * \"permissions\": {\n * \"oauth\": {\n * \"required\": [\n * { \"provider\": \"github\", \"scopes\": [\"read:user\"] }\n * ],\n * \"optional\": [\n * { \"provider\": \"github\", \"scopes\": [\"repo\"] }\n * ]\n * }\n * }\n * }\n * ```\n */\n oauth?: {\n /** OAuth providers required for the addon to function */\n required?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n /** OAuth providers that enhance functionality but aren't required */\n optional?: Array<{\n provider: OAuthProvider\n scopes: string[]\n /** Reason shown to user when requesting this permission */\n reason?: string\n }>\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\n/**\n * Validation result type.\n */\nexport interface ValidationResult {\n success: boolean\n errors?: string[]\n manifest?: AddonManifest\n}\n\n/**\n * Validates an addon manifest object.\n *\n * @param input - The manifest object to validate\n * @returns ValidationResult with success status and any errors\n *\n * @example\n * ```typescript\n * const result = validateManifest(myManifest)\n * if (!result.success) {\n * result.errors?.forEach(err => console.error(err))\n * }\n * ```\n */\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: string[] = []\n\n if (!input || typeof input !== 'object') {\n return { success: false, errors: ['Manifest must be an object'] }\n }\n\n const manifest = input as Record<string, unknown>\n\n // Required fields\n if (typeof manifest.name !== 'string' || manifest.name.trim() === '') {\n errors.push('name: Required and must be a non-empty string')\n }\n\n if (typeof manifest.version !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.version)) {\n errors.push('version: Required and must be a valid semver (e.g., \"1.0.0\")')\n }\n\n if (typeof manifest.sdkVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+/.test(manifest.sdkVersion)) {\n errors.push('sdkVersion: Required and must be a valid semver (e.g., \"2.2.0\")')\n }\n\n // Optional string fields\n const optionalStrings = ['description', 'author', 'type', 'license', 'homepage', 'repository']\n for (const field of optionalStrings) {\n if (manifest[field] !== undefined && typeof manifest[field] !== 'string') {\n errors.push(`${field}: Must be a string`)\n }\n }\n\n // Categories\n if (manifest.categories !== undefined) {\n if (!Array.isArray(manifest.categories)) {\n errors.push('categories: Must be an array of strings')\n } else if (!manifest.categories.every(c => typeof c === 'string')) {\n errors.push('categories: All items must be strings')\n }\n }\n\n // Settings validation\n if (manifest.settings !== undefined) {\n if (typeof manifest.settings !== 'object' || manifest.settings === null) {\n errors.push('settings: Must be an object')\n } else {\n const settings = manifest.settings as Record<string, unknown>\n for (const [key, value] of Object.entries(settings)) {\n if (typeof value !== 'object' || value === null) {\n errors.push(`settings.${key}: Must be an object`)\n continue\n }\n\n const setting = value as Record<string, unknown>\n const validTypes: SettingType[] = [\n 'string', 'number', 'boolean', 'color', 'select', 'range',\n 'image', 'video', 'audio', 'file', 'section', 'textarea', 'radio', 'gradient', 'vector2',\n 'button', 'multi-select'\n ]\n\n if (!validTypes.includes(setting.type as SettingType)) {\n errors.push(`settings.${key}.type: Must be one of: ${validTypes.join(', ')}`)\n }\n\n // Type-specific validation\n if (['select', 'radio', 'multi-select'].includes(setting.type as string)) {\n if (!Array.isArray(setting.options) || setting.options.length === 0) {\n errors.push(`settings.${key}.options: Required for ${setting.type} type`)\n }\n }\n\n if (setting.type === 'range') {\n if (typeof setting.min !== 'number' || typeof setting.max !== 'number') {\n errors.push(`settings.${key}: range type requires min and max`)\n }\n }\n }\n }\n }\n\n // Capabilities validation\n if (manifest.capabilities !== undefined) {\n if (typeof manifest.capabilities !== 'object' || manifest.capabilities === null) {\n errors.push('capabilities: Must be an object')\n } else {\n const caps = manifest.capabilities as Record<string, unknown>\n\n if (caps.hotReload !== undefined && typeof caps.hotReload !== 'boolean') {\n errors.push('capabilities.hotReload: Must be a boolean')\n }\n\n if (caps.systemEvents !== undefined) {\n const validEvents: SystemEventType[] = [\n 'viewport:resize', 'theme:change', 'visibility:change',\n 'layer:focus', 'layer:blur', 'app:idle', 'app:active'\n ]\n\n if (!Array.isArray(caps.systemEvents)) {\n errors.push('capabilities.systemEvents: Must be an array')\n } else {\n for (const event of caps.systemEvents) {\n if (!validEvents.includes(event)) {\n errors.push(`capabilities.systemEvents: Invalid event \"${event}\"`)\n }\n }\n }\n }\n }\n }\n\n // Permissions validation\n if (manifest.permissions !== undefined) {\n if (typeof manifest.permissions !== 'object' || manifest.permissions === null) {\n errors.push('permissions: Must be an object')\n } else {\n const perms = manifest.permissions as Record<string, unknown>\n\n if (perms.storage !== undefined) {\n const storage = perms.storage as Record<string, unknown>\n if (typeof storage?.quota !== 'number' || storage.quota <= 0) {\n errors.push('permissions.storage.quota: Must be a positive number')\n }\n }\n\n if (perms.cpu !== undefined) {\n const cpu = perms.cpu as Record<string, unknown>\n if (!['low', 'medium', 'high'].includes(cpu?.limit as string)) {\n errors.push('permissions.cpu.limit: Must be low, medium, or high')\n }\n }\n\n if (perms.network !== undefined) {\n const network = perms.network as Record<string, unknown>\n if (!Array.isArray(network?.domains)) {\n errors.push('permissions.network.domains: Must be an array of domain strings')\n }\n }\n }\n }\n\n // Default layout validation\n if (manifest.defaultLayout !== undefined) {\n if (typeof manifest.defaultLayout !== 'object' || manifest.defaultLayout === null) {\n errors.push('defaultLayout: Must be an object')\n } else {\n const layout = manifest.defaultLayout as Record<string, unknown>\n const numFields = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent', 'rotation']\n for (const field of numFields) {\n if (layout[field] !== undefined && typeof layout[field] !== 'number') {\n errors.push(`defaultLayout.${field}: Must be a number`)\n }\n }\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors }\n }\n\n return { success: true, manifest: manifest as unknown as AddonManifest }\n}\n\n/**\n * Type guard to check if an object is a valid AddonManifest.\n *\n * @param input - Object to check\n * @returns True if input is a valid AddonManifest\n */\nexport function isValidManifest(input: unknown): input is AddonManifest {\n return validateManifest(input).success\n}\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Check if manifest declares hot-reload capability.\n */\nexport function supportsHotReload(manifest: AddonManifest): boolean {\n return manifest.capabilities?.hotReload === true\n}\n\n/**\n * Get system events the addon subscribes to.\n */\nexport function getSubscribedEvents(manifest: AddonManifest): SystemEventType[] {\n return manifest.capabilities?.systemEvents ?? []\n}\n\n/**\n * Get required permissions from manifest.\n */\nexport function getRequiredPermissions(manifest: AddonManifest): PermissionType[] {\n const permissions: PermissionType[] = []\n\n if (manifest.permissions?.storage) permissions.push('storage')\n if (manifest.permissions?.cpu) permissions.push('cpu-high')\n if (manifest.permissions?.network) permissions.push('network')\n if (manifest.permissions?.audio) permissions.push('audio')\n if (manifest.permissions?.notifications) permissions.push('notifications')\n\n return permissions\n}\n"]}
|
package/package.json
CHANGED