@camstack/addon-provider-frigate 0.1.2 → 0.1.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/addon.ts","../src/frigate-api.ts","../src/frigate-mqtt.ts","../src/frigate-device.ts","../src/element-config-store.ts","../src/frigate-provider.ts"],"sourcesContent":["import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { FrigateProvider } from './frigate-provider'\nimport type { FrigateProviderConfig } from './frigate-provider'\n\nexport class FrigateProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-frigate',\n name: 'Frigate NVR Provider',\n version: '0.1.0',\n description: 'Integrazione con Frigate NVR per camere e detection',\n capabilities: ['device-provider'],\n }\n\n private provider: FrigateProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as FrigateProviderConfig\n if (!config.url) {\n context.logger.warn('Frigate provider: no URL configured, skipping initialization')\n return\n }\n\n const providerConfig: FrigateProviderConfig = {\n id: config.id ?? 'frigate-default',\n name: config.name ?? 'Frigate NVR',\n url: config.url,\n username: config.username,\n password: config.password,\n mqtt: config.mqtt,\n }\n\n this.provider = new FrigateProvider(providerConfig, {\n id: context.id,\n logger: context.logger,\n eventBus: context.eventBus,\n storage: context.storage,\n config: context.config,\n })\n\n context.logger.info('Frigate provider addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.provider?.stop()\n this.provider = null\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'device-provider' && this.provider) {\n return this.provider as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'connection',\n title: 'Frigate Connection',\n description: 'Configure the connection to your Frigate NVR instance',\n columns: 2,\n fields: [\n { type: 'text', key: 'url', label: 'Frigate URL', required: true, placeholder: 'http://frigate:5000' },\n { type: 'text', key: 'name', label: 'Display Name', placeholder: 'Frigate NVR' },\n { type: 'text', key: 'username', label: 'Username' },\n { type: 'password', key: 'password', label: 'Password', showToggle: true },\n ],\n },\n {\n id: 'mqtt',\n title: 'MQTT Settings',\n description: 'Optional: Connect to Frigate MQTT for real-time events',\n style: 'accordion',\n defaultCollapsed: true,\n columns: 2,\n fields: [\n { type: 'text', key: 'mqtt.brokerUrl', label: 'MQTT Broker URL', placeholder: 'mqtt://broker:1883' },\n { type: 'text', key: 'mqtt.topicPrefix', label: 'Topic Prefix', placeholder: 'frigate' },\n { type: 'text', key: 'mqtt.username', label: 'MQTT Username' },\n { type: 'password', key: 'mqtt.password', label: 'MQTT Password', showToggle: true },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return {}\n }\n\n async onConfigChange(_config: Record<string, unknown>): Promise<void> {\n // Restart provider with new config\n }\n}\n","import type {\n FrigateConfig,\n FrigateMotionData,\n FrigateRawEvent,\n FrigateRawRecording,\n FrigateReviewItem,\n} from './frigate-types'\n\nexport interface FrigateApiConfig {\n baseUrl: string\n username?: string\n password?: string\n}\n\nexport interface EventsQuery {\n after?: number\n before?: number\n cameras?: string\n limit?: number\n labels?: string\n}\n\nexport interface ReviewQuery {\n after?: number\n before?: number\n cameras?: string\n limit?: number\n}\n\nexport interface MotionQuery {\n after: number\n before: number\n cameras?: string\n}\n\nexport interface TestConnectionResult {\n success: boolean\n version?: string\n cameraCount?: number\n error?: string\n}\n\nexport class FrigateApiClient {\n private readonly baseUrl: string\n private authToken: string | null = null\n private authHeader: string | null = null\n\n constructor(private readonly config: FrigateApiConfig) {\n this.baseUrl = (config.baseUrl ?? '').replace(/\\/+$/, '')\n }\n\n private async authenticate(): Promise<void> {\n const { username, password } = this.config\n\n if (!username || !password) {\n return\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/api/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ user: username, password }),\n })\n\n if (response.ok) {\n const cookies = response.headers.get('set-cookie')\n if (cookies) {\n this.authToken = cookies\n return\n }\n }\n } catch {\n // Token login failed, fall through to Basic auth\n }\n\n this.authHeader = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`\n }\n\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {}\n\n if (this.authToken) {\n headers['Cookie'] = this.authToken\n } else if (this.authHeader) {\n headers['Authorization'] = this.authHeader\n }\n\n return headers\n }\n\n private async request<T>(path: string, options?: RequestInit): Promise<T> {\n if ((this.config.username || this.config.password) && !this.authToken && !this.authHeader) {\n await this.authenticate()\n }\n\n const url = `${this.baseUrl}${path}`\n const response = await fetch(url, {\n ...options,\n headers: {\n ...this.buildHeaders(),\n ...options?.headers,\n },\n })\n\n if (!response.ok) {\n throw new Error(`Frigate API error: ${response.status} ${response.statusText} for ${path}`)\n }\n\n return response.json() as Promise<T>\n }\n\n private async requestBuffer(path: string): Promise<Buffer> {\n if ((this.config.username || this.config.password) && !this.authToken && !this.authHeader) {\n await this.authenticate()\n }\n\n const url = `${this.baseUrl}${path}`\n const response = await fetch(url, {\n headers: this.buildHeaders(),\n })\n\n if (!response.ok) {\n throw new Error(`Frigate API error: ${response.status} ${response.statusText} for ${path}`)\n }\n\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n }\n\n private async requestText(path: string, options?: RequestInit): Promise<string> {\n if ((this.config.username || this.config.password) && !this.authToken && !this.authHeader) {\n await this.authenticate()\n }\n\n const url = `${this.baseUrl}${path}`\n const response = await fetch(url, {\n ...options,\n headers: {\n ...this.buildHeaders(),\n ...options?.headers,\n },\n })\n\n if (!response.ok) {\n throw new Error(`Frigate API error: ${response.status} ${response.statusText} for ${path}`)\n }\n\n return response.text()\n }\n\n // --- Config & Discovery ---\n\n async getConfig(): Promise<FrigateConfig> {\n return this.request<FrigateConfig>('/api/config')\n }\n\n async getGo2rtcStreams(): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>('/api/go2rtc/streams')\n }\n\n // --- Events ---\n\n async getEvents(params: EventsQuery): Promise<FrigateRawEvent[]> {\n const query = buildQueryString(params)\n return this.request<FrigateRawEvent[]>(`/api/events${query}`)\n }\n\n async getReviewItems(params: ReviewQuery): Promise<FrigateReviewItem[]> {\n const query = buildQueryString(params)\n return this.request<FrigateReviewItem[]>(`/api/review${query}`)\n }\n\n // --- Recordings ---\n\n async getRecordings(\n camera: string,\n after: number,\n before: number,\n ): Promise<FrigateRawRecording[]> {\n const query = buildQueryString({ after, before })\n return this.request<FrigateRawRecording[]>(`/api/${encodeURIComponent(camera)}/recordings${query}`)\n }\n\n async getMotionActivity(params: MotionQuery): Promise<FrigateMotionData[]> {\n const query = buildQueryString(params)\n return this.request<FrigateMotionData[]>(`/api/motion_activity${query}`)\n }\n\n // --- Media ---\n\n async getLatestSnapshot(camera: string): Promise<Buffer> {\n return this.requestBuffer(`/api/${encodeURIComponent(camera)}/latest.jpg`)\n }\n\n async getEventThumbnail(eventId: string): Promise<Buffer> {\n return this.requestBuffer(`/api/events/${encodeURIComponent(eventId)}/thumbnail.jpg`)\n }\n\n async getEventSnapshot(eventId: string): Promise<Buffer> {\n return this.requestBuffer(`/api/events/${encodeURIComponent(eventId)}/snapshot.jpg`)\n }\n\n async getEventClip(eventId: string): Promise<Buffer> {\n return this.requestBuffer(`/api/events/${encodeURIComponent(eventId)}/clip.mp4`)\n }\n\n async getRecordingThumbnail(camera: string, timestampSec: number): Promise<Buffer> {\n return this.requestBuffer(\n `/api/${encodeURIComponent(camera)}/recordings/thumbnail-${timestampSec}.jpg`,\n )\n }\n\n // --- WebRTC ---\n\n async proxyWhepSdp(streamName: string, sdpOffer: string): Promise<string> {\n return this.requestText(`/api/go2rtc/webrtc?src=${encodeURIComponent(streamName)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/sdp' },\n body: sdpOffer,\n })\n }\n\n // --- PTZ ---\n\n async getPtzInfo(\n camera: string,\n ): Promise<{ features?: string[]; presets?: string[] } | null> {\n try {\n return await this.request<{ features?: string[]; presets?: string[] }>(\n `/api/${encodeURIComponent(camera)}/ptz/info`,\n )\n } catch {\n return null\n }\n }\n\n async ptzCommand(\n camera: string,\n command: string,\n params?: Record<string, unknown>,\n ): Promise<void> {\n const query = buildQueryString({ ...params, command })\n await this.request<void>(`/api/${encodeURIComponent(camera)}/ptz${query}`)\n }\n\n // --- Test Connection ---\n\n async testConnection(): Promise<TestConnectionResult> {\n try {\n const config = await this.getConfig()\n const cameraNames = Object.keys(config.cameras ?? {})\n return {\n success: true,\n version: config.version,\n cameraCount: cameraNames.length,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n }\n}\n\n// --- Utility ---\n\nexport function buildQueryString(params: Record<string, unknown> | object): string {\n const entries = Object.entries(params).filter(\n ([, v]) => v !== undefined && v !== null,\n )\n if (entries.length === 0) {\n return ''\n }\n const searchParams = new URLSearchParams()\n for (const [key, value] of entries) {\n searchParams.set(key, String(value))\n }\n return `?${searchParams.toString()}`\n}\n","import type { MqttClient } from 'mqtt'\nimport { connect } from 'mqtt'\nimport type { LiveEvent } from '@camstack/types'\n\nexport interface FrigateMqttConfig {\n brokerUrl: string\n username?: string\n password?: string\n topicPrefix: string\n}\n\nexport interface MqttStatus {\n connected: boolean\n eventCount: number\n}\n\nexport class FrigateMqttClient {\n private client: MqttClient | null = null\n private readonly listeners: Set<(event: LiveEvent) => void> = new Set()\n private eventCount = 0\n\n constructor(\n private readonly config: FrigateMqttConfig,\n private readonly cameraResolutions: Map<string, { width: number; height: number }>,\n ) {}\n\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const { brokerUrl, username, password, topicPrefix } = this.config\n\n this.client = connect(brokerUrl, {\n username,\n password,\n reconnectPeriod: 5000,\n connectTimeout: 10000,\n })\n\n this.client.on('connect', () => {\n const prefix = topicPrefix\n this.client!.subscribe([\n `${prefix}/events`,\n `${prefix}/reviews`,\n `${prefix}/+/motion/state`,\n `${prefix}/+/audio/+`,\n ])\n resolve()\n })\n\n this.client.on('error', (err) => {\n reject(err)\n })\n\n this.client.on('message', (topic: string, payload: Buffer) => {\n const event = parseMqttMessage(\n topic,\n payload,\n topicPrefix,\n this.cameraResolutions,\n )\n if (event) {\n this.eventCount++\n for (const listener of this.listeners) {\n listener(event)\n }\n }\n })\n })\n }\n\n async disconnect(): Promise<void> {\n if (this.client) {\n await this.client.endAsync()\n this.client = null\n }\n this.listeners.clear()\n }\n\n subscribe(callback: (event: LiveEvent) => void): () => void {\n this.listeners.add(callback)\n return () => {\n this.listeners.delete(callback)\n }\n }\n\n get status(): MqttStatus {\n return {\n connected: this.client?.connected ?? false,\n eventCount: this.eventCount,\n }\n }\n}\n\n// --- Exported for testing ---\n\ninterface FrigateMqttEventPayload {\n type?: 'new' | 'update' | 'end'\n before?: FrigateMqttDetectionState\n after?: FrigateMqttDetectionState\n}\n\ninterface FrigateMqttDetectionState {\n id?: string\n camera?: string\n label?: string\n sub_label?: string\n top_score?: number\n start_time?: number\n end_time?: number | null\n current_zones?: string[]\n has_snapshot?: boolean\n has_clip?: boolean\n box?: [number, number, number, number]\n}\n\ninterface FrigateMqttReviewPayload {\n type?: 'new' | 'update' | 'end'\n before?: FrigateMqttReviewState\n after?: FrigateMqttReviewState\n}\n\ninterface FrigateMqttReviewState {\n id?: string\n camera?: string\n start_time?: number\n end_time?: number | null\n severity?: 'alert' | 'detection'\n data?: {\n objects?: string[]\n zones?: string[]\n sub_labels?: string[]\n }\n}\n\nexport function parseMqttMessage(\n topic: string,\n payload: Buffer,\n prefix: string,\n resolutions: Map<string, { width: number; height: number }>,\n): LiveEvent | null {\n const topicParts = topic.split('/')\n const prefixParts = prefix.split('/')\n const relative = topicParts.slice(prefixParts.length)\n\n // {prefix}/events\n if (relative.length === 1 && relative[0] === 'events') {\n return parseDetectionEvent(payload, resolutions)\n }\n\n // {prefix}/reviews\n if (relative.length === 1 && relative[0] === 'reviews') {\n return parseReviewEvent(payload)\n }\n\n // {prefix}/{camera}/motion/state\n if (relative.length === 3 && relative[1] === 'motion' && relative[2] === 'state') {\n const camera = relative[0]!\n return parseMotionEvent(camera, payload)\n }\n\n // {prefix}/{camera}/audio/{metric}\n if (relative.length === 3 && relative[1] === 'audio') {\n const camera = relative[0]!\n const metric = relative[2]!\n return parseAudioEvent(camera, metric, payload)\n }\n\n return null\n}\n\nfunction parseDetectionEvent(\n payload: Buffer,\n resolutions: Map<string, { width: number; height: number }>,\n): LiveEvent | null {\n try {\n const parsed: FrigateMqttEventPayload = JSON.parse(payload.toString())\n const state = parsed.after ?? parsed.before\n if (!state?.camera || !state?.label) {\n return null\n }\n\n const data: Record<string, unknown> = {\n eventType: parsed.type ?? 'update',\n id: state.id,\n label: state.label,\n subLabel: state.sub_label,\n score: state.top_score,\n startTime: state.start_time,\n endTime: state.end_time,\n zones: state.current_zones,\n hasSnapshot: state.has_snapshot,\n hasClip: state.has_clip,\n }\n\n if (state.box) {\n const resolution = resolutions.get(state.camera)\n if (resolution) {\n data.boundingBox = normalizeBoundingBox(state.box, resolution.width, resolution.height)\n } else {\n data.rawBox = state.box\n }\n }\n\n return {\n type: 'detection',\n camera: state.camera,\n timestamp: Date.now(),\n data,\n }\n } catch {\n return null\n }\n}\n\nfunction parseReviewEvent(payload: Buffer): LiveEvent | null {\n try {\n const parsed: FrigateMqttReviewPayload = JSON.parse(payload.toString())\n const state = parsed.after ?? parsed.before\n if (!state?.camera) {\n return null\n }\n\n return {\n type: 'review',\n camera: state.camera,\n timestamp: Date.now(),\n data: {\n eventType: parsed.type ?? 'update',\n id: state.id,\n severity: state.severity,\n startTime: state.start_time,\n endTime: state.end_time,\n objects: state.data?.objects,\n zones: state.data?.zones,\n subLabels: state.data?.sub_labels,\n },\n }\n } catch {\n return null\n }\n}\n\nfunction parseMotionEvent(camera: string, payload: Buffer): LiveEvent {\n const value = payload.toString().trim()\n const active = value === 'ON'\n\n return {\n type: 'motion',\n camera,\n timestamp: Date.now(),\n data: { active },\n }\n}\n\nfunction parseAudioEvent(camera: string, metric: string, payload: Buffer): LiveEvent | null {\n const raw = payload.toString().trim()\n const value = parseFloat(raw)\n\n if (isNaN(value)) {\n return null\n }\n\n return {\n type: 'audio',\n camera,\n timestamp: Date.now(),\n data: { metric, value },\n }\n}\n\n/**\n * Normalize Frigate bounding box from pixel coordinates to 0-1 range.\n * Frigate box format: [y_min, x_min, y_max, x_max] in pixels.\n * Output: { x, y, w, h } normalized to 0-1 range.\n */\nexport function normalizeBoundingBox(\n box: [number, number, number, number],\n width: number,\n height: number,\n): { x: number; y: number; w: number; h: number } {\n const [yMin, xMin, yMax, xMax] = box\n return {\n x: xMin / width,\n y: yMin / height,\n w: (xMax - xMin) / width,\n h: (yMax - yMin) / height,\n }\n}\n","import { DeviceType } from '@camstack/types'\nimport type {\n IDevice,\n DeviceState,\n DeviceMetadata,\n DeviceCapabilityName,\n IDeviceCapability,\n CamstackContext,\n ICamera,\n StreamOption,\n ConnectionMode,\n IMotionSensor,\n IObjectDetector,\n DetectionZone,\n DetectionLine,\n IAudioDetector,\n IEvents,\n EventQuery,\n EventQueryResult,\n DeviceStoredEvent,\n IRecording,\n RecordingSegment,\n TimeRange,\n} from '@camstack/types'\nimport type { FrigateApiClient } from './frigate-api'\n\nexport interface FrigateDeviceConfig {\n readonly cameraName: string\n readonly providerId: string\n readonly detectWidth: number\n readonly detectHeight: number\n readonly audioEnabled: boolean\n readonly recordEnabled: boolean\n readonly ptzEnabled: boolean\n readonly streams: StreamOption[]\n readonly frigateHost: string\n}\n\nexport class FrigateDevice implements IDevice {\n readonly id: string\n readonly name: string\n readonly providerId: string\n readonly type: DeviceType = DeviceType.Camera\n readonly capabilities: DeviceCapabilityName[]\n\n private readonly capabilityMap = new Map<DeviceCapabilityName, IDeviceCapability>()\n\n readonly ctx: CamstackContext\n\n constructor(\n private readonly config: FrigateDeviceConfig,\n private readonly api: FrigateApiClient,\n ctx: CamstackContext,\n ) {\n this.id = `${config.providerId}/${config.cameraName}`\n this.name = config.cameraName\n this.providerId = config.providerId\n this.ctx = ctx\n\n // Capabilities based on camera config\n const caps: DeviceCapabilityName[] = ['camera', 'events', 'recording', 'motionSensor', 'objectDetector']\n if (config.audioEnabled) caps.push('audioDetector')\n this.capabilities = caps\n\n this.capabilityMap.set('camera', this.createCamera())\n this.capabilityMap.set('motionSensor', this.createMotionSensor())\n this.capabilityMap.set('objectDetector', this.createObjectDetector())\n this.capabilityMap.set('events', this.createEvents())\n this.capabilityMap.set('recording', this.createRecording())\n if (config.audioEnabled) {\n this.capabilityMap.set('audioDetector', this.createAudioDetector())\n }\n }\n\n getCapability<T extends IDeviceCapability>(cap: DeviceCapabilityName): T | null {\n return (this.capabilityMap.get(cap) as T) ?? null\n }\n\n hasCapability(cap: DeviceCapabilityName): boolean {\n return this.capabilityMap.has(cap)\n }\n\n getState(): DeviceState {\n return { online: true }\n }\n\n getMetadata(): DeviceMetadata {\n return { manufacturer: 'Frigate NVR' }\n }\n\n // --- Capability Factories ---\n\n private createCamera(): ICamera {\n const { api, config } = this\n return {\n kind: 'camera',\n async getSnapshot() { return api.getLatestSnapshot(config.cameraName) },\n async getStreamOptions(): Promise<StreamOption[]> {\n return config.streams.map(s => ({\n ...s,\n url: s.url ?? `rtsp://${config.frigateHost}:8554/${s.id}`,\n }))\n },\n async getStreamUrl(option: StreamOption) {\n return `/api/go2rtc/webrtc?src=${encodeURIComponent(option.id)}`\n },\n getConnectionMode(): ConnectionMode { return 'on-demand' },\n async setConnectionMode() {},\n }\n }\n\n private createMotionSensor(): IMotionSensor {\n let motionActive = false\n return {\n kind: 'motionSensor',\n isMotionDetected() { return motionActive },\n subscribe() { return () => {} }, // real-time via MQTT through provider\n }\n }\n\n private createObjectDetector(): IObjectDetector {\n const zones: DetectionZone[] = []\n const lines: DetectionLine[] = []\n\n return {\n kind: 'objectDetector',\n getLabels() { return [] },\n onDetections() { return () => {} },\n\n // Zone management\n async getZones() { return zones },\n async addZone(zone) { zones.push(zone) },\n async removeZone(id) { const i = zones.findIndex(z => z.id === id); if (i >= 0) zones.splice(i, 1) },\n async updateZone(id, partial) { const z = zones.find(z => z.id === id); if (z) Object.assign(z, partial) },\n\n // Line management\n async getLines() { return lines },\n async addLine(line) { lines.push(line) },\n async removeLine(id) { const i = lines.findIndex(l => l.id === id); if (i >= 0) lines.splice(i, 1) },\n\n // Active tracking (populated by MQTT events in real-time)\n getActiveDetections() { return [] },\n getZoneDetections() { return [] },\n getStationaryDetections() { return [] },\n getMovingDetections() { return [] },\n }\n }\n\n private createEvents(): IEvents {\n const { api, config } = this\n return {\n kind: 'events',\n\n async getEvents(query: EventQuery): Promise<EventQueryResult> {\n const raw = await api.getEvents({\n cameras: config.cameraName,\n after: query.since,\n before: query.until,\n limit: query.limit,\n labels: query.detectionClasses?.join(','),\n })\n\n const events: DeviceStoredEvent[] = raw.map(e => ({\n id: e.id,\n type: 'detection',\n timestamp: e.start_time * 1000, // Frigate uses seconds, we use ms\n endTimestamp: e.end_time ? e.end_time * 1000 : undefined,\n label: e.label,\n score: e.top_score,\n hasClip: e.has_clip,\n hasSnapshot: e.has_snapshot,\n thumbnailUrl: `/api/events/${e.id}/thumbnail.jpg`,\n }))\n\n return { events, total: events.length }\n },\n\n async getEventThumbnail(eventId: string) {\n try { return await api.getEventThumbnail(eventId) }\n catch { return null }\n },\n\n async getEventSnapshot(eventId: string) {\n try { return await api.getEventSnapshot(eventId) }\n catch { return null }\n },\n\n async getEventClipUrl(eventId: string) {\n return `/api/events/${encodeURIComponent(eventId)}/clip.mp4`\n },\n }\n }\n\n private createRecording(): IRecording {\n const { api, config } = this\n return {\n kind: 'recording',\n\n async getSegments(range: TimeRange): Promise<RecordingSegment[]> {\n const recs = await api.getRecordings(config.cameraName, range.since, range.until)\n return recs.map(r => ({\n id: r.id ?? `${config.cameraName}-${r.start_time}`,\n startTime: r.start_time * 1000,\n endTime: r.end_time * 1000,\n duration: r.duration,\n }))\n },\n\n async getPlaybackUrl(startTime: number, endTime: number) {\n const startSec = Math.floor(startTime / 1000)\n const endSec = Math.floor(endTime / 1000)\n return `/api/${encodeURIComponent(config.cameraName)}/start/${startSec}/end/${endSec}/clip.mp4`\n },\n\n async getThumbnailAt(timestampMs: number) {\n try { return await api.getRecordingThumbnail(config.cameraName, Math.floor(timestampMs / 1000)) }\n catch { return null }\n },\n }\n }\n\n private createAudioDetector(): IAudioDetector {\n return {\n kind: 'audioDetector',\n getAudioLevel() { return 0 },\n subscribe() { return () => {} }, // real-time via MQTT through provider\n }\n }\n}\n","import type { IElementConfig } from '@camstack/types'\nimport type { IStorageLocation } from '@camstack/types'\n\n/**\n * Persisted config store for a single element.\n * Reads/writes to the element's scoped storage under the 'config' collection.\n * Notifies listeners on every change.\n */\nexport class ElementConfigStore implements IElementConfig {\n private cache: Record<string, unknown> = {}\n private listeners: Set<(config: Record<string, unknown>) => void> = new Set()\n private loaded = false\n\n constructor(\n private readonly elementId: string,\n private readonly storage: IStorageLocation,\n ) {}\n\n /** Load config from storage into cache. Called once on first access. */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n if (!this.storage.structured) {\n this.loaded = true\n return\n }\n\n try {\n const records = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n if (records.length > 0) {\n this.cache = (records[0] as any).data ?? {}\n }\n } catch {\n // Storage might not be ready yet\n }\n this.loaded = true\n }\n\n getAll(): Record<string, unknown> {\n return { ...this.cache }\n }\n\n get<T = unknown>(key: string): T | undefined {\n const parts = key.split('.')\n let current: unknown = this.cache\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n return current as T | undefined\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await this.ensureLoaded()\n setNestedValue(this.cache, key, value)\n await this.persist()\n this.notifyListeners()\n }\n\n async setAll(config: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n this.cache = { ...config }\n await this.persist()\n this.notifyListeners()\n }\n\n onChange(callback: (config: Record<string, unknown>) => void): () => void {\n this.listeners.add(callback)\n return () => { this.listeners.delete(callback) }\n }\n\n /** Initialize from storage — called by ContextFactory after creation */\n async load(): Promise<void> {\n await this.ensureLoaded()\n }\n\n /** Initialize with default values (doesn't overwrite existing) */\n async loadDefaults(defaults: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n if (Object.keys(this.cache).length === 0) {\n this.cache = { ...defaults }\n await this.persist()\n }\n }\n\n private async persist(): Promise<void> {\n if (!this.storage.structured) return\n\n try {\n const existing = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n\n if (existing.length > 0) {\n await this.storage.structured.update('config', this.elementId, this.cache)\n } else {\n await this.storage.structured.insert({\n collection: 'config',\n id: this.elementId,\n data: this.cache,\n })\n }\n } catch {\n // Storage might not be ready\n }\n }\n\n private notifyListeners(): void {\n const snapshot = this.getAll()\n for (const listener of this.listeners) {\n try {\n listener(snapshot)\n } catch {\n // Don't let one bad listener kill others\n }\n }\n }\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part] as Record<string, unknown>\n }\n current[parts[parts.length - 1]!] = value\n}\n","import { FrigateApiClient } from './frigate-api'\nimport { FrigateMqttClient } from './frigate-mqtt'\nimport { FrigateDevice } from './frigate-device'\nimport { ElementConfigStore } from './element-config-store'\nimport type { StreamOption } from '@camstack/types'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n} from '@camstack/types'\nimport type { IDevice } from '@camstack/types'\nimport type { CamstackContext } from '@camstack/types'\nimport type { FrigateConfig, FrigateCameraConfig } from './frigate-types'\n\nexport interface FrigateProviderConfig {\n readonly id: string\n readonly name: string\n readonly url: string\n readonly username?: string\n readonly password?: string\n readonly mqtt?: {\n readonly brokerUrl: string\n readonly username?: string\n readonly password?: string\n readonly topicPrefix?: string\n }\n}\n\nconst ADOPTED_DEVICES_KEY = 'adoptedDevices'\n\nexport class FrigateProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'frigate'\n readonly name: string\n readonly discoveryMode: 'auto' = 'auto'\n readonly ctx: CamstackContext\n\n private readonly api: FrigateApiClient\n private mqtt: FrigateMqttClient | null = null\n private devices: readonly FrigateDevice[] = []\n private readonly liveEventListeners: Set<(event: LiveEvent) => void> = new Set()\n private mqttUnsubscribe?: () => void\n\n /** Cached Frigate config, refreshed on start() and discoverDevices() */\n private frigateConfig: FrigateConfig | null = null\n private go2rtcStreams: Record<string, unknown> = {}\n private frigateHost: string\n\n constructor(\n private readonly config: FrigateProviderConfig,\n ctx: CamstackContext,\n ) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n this.api = new FrigateApiClient({\n baseUrl: config.url,\n username: config.username,\n password: config.password,\n })\n this.frigateHost = new URL(config.url).hostname\n }\n\n async start(): Promise<void> {\n this.ctx.logger.info(`Starting Frigate provider: ${this.config.url}`)\n\n // 1. Fetch Frigate config to discover cameras\n this.frigateConfig = await this.api.getConfig()\n this.go2rtcStreams = await this.api.getGo2rtcStreams()\n\n // 2. Build camera resolutions map (for MQTT bbox normalization)\n const resolutions = new Map<string, { width: number; height: number }>()\n for (const [name, cam] of Object.entries(this.frigateConfig.cameras)) {\n if (cam.detect) {\n resolutions.set(name, { width: cam.detect.width, height: cam.detect.height })\n }\n }\n\n // 3. Load only previously adopted cameras\n const adoptedIds = this.getAdoptedDeviceIds()\n if (adoptedIds.length > 0) {\n const devices: FrigateDevice[] = []\n for (const cameraName of adoptedIds) {\n const cam = this.frigateConfig.cameras[cameraName]\n if (!cam || cam.enabled === false) {\n this.ctx.logger.warn(`Adopted camera \"${cameraName}\" no longer available in Frigate config, skipping`)\n continue\n }\n devices.push(this.createDeviceFromCamera(cameraName, cam))\n }\n this.devices = devices\n this.ctx.logger.info(`Loaded ${devices.length} adopted cameras`)\n } else {\n this.ctx.logger.info('No adopted cameras yet — use discoverDevices() + adoptDevice() to import cameras')\n }\n\n // 4. Connect MQTT if configured\n if (this.config.mqtt?.brokerUrl) {\n this.mqtt = new FrigateMqttClient(\n {\n brokerUrl: this.config.mqtt.brokerUrl,\n username: this.config.mqtt.username,\n password: this.config.mqtt.password,\n topicPrefix: this.config.mqtt.topicPrefix ?? 'frigate',\n },\n resolutions,\n )\n\n await this.mqtt.connect()\n this.mqttUnsubscribe = this.mqtt.subscribe((event) => {\n for (const listener of this.liveEventListeners) {\n listener(event)\n }\n })\n this.ctx.logger.info('MQTT connected')\n }\n }\n\n async stop(): Promise<void> {\n this.ctx.logger.info('Stopping Frigate provider')\n this.mqttUnsubscribe?.()\n this.mqttUnsubscribe = undefined\n await this.mqtt?.disconnect()\n this.mqtt = null\n this.devices = []\n this.frigateConfig = null\n this.go2rtcStreams = {}\n }\n\n getStatus(): ProviderStatus {\n return {\n connected: this.mqtt?.status.connected ?? false,\n deviceCount: this.devices.length,\n }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n // Refresh from Frigate API to get the latest camera list\n this.frigateConfig = await this.api.getConfig()\n this.go2rtcStreams = await this.api.getGo2rtcStreams()\n\n const adoptedIds = this.getAdoptedDeviceIds()\n const discovered: DiscoveredDevice[] = []\n\n for (const [name, cam] of Object.entries(this.frigateConfig.cameras)) {\n if (cam.enabled === false) continue\n\n // Build capabilities list to match what FrigateDevice would produce\n const caps: DiscoveredDevice['capabilities'] = ['camera', 'events', 'recording', 'motionSensor', 'objectDetector']\n if (cam.audio?.enabled) caps.push('audioDetector')\n\n discovered.push({\n externalId: name,\n name,\n type: 'camera',\n capabilities: caps,\n metadata: { manufacturer: 'Frigate NVR' },\n })\n }\n\n return discovered\n }\n\n async adoptDevice(externalId: string, _config?: Record<string, unknown>): Promise<IDevice> {\n // 1. Ensure we have a fresh Frigate config\n if (!this.frigateConfig) {\n this.frigateConfig = await this.api.getConfig()\n this.go2rtcStreams = await this.api.getGo2rtcStreams()\n }\n\n // 2. Find the camera in Frigate config\n const cam = this.frigateConfig.cameras[externalId]\n if (!cam) {\n throw new Error(`Camera \"${externalId}\" not found in Frigate config`)\n }\n if (cam.enabled === false) {\n throw new Error(`Camera \"${externalId}\" is disabled in Frigate config`)\n }\n\n // 3. Check if already adopted\n const existing = this.devices.find((d) => d.name === externalId)\n if (existing) {\n this.ctx.logger.info(`Camera \"${externalId}\" is already adopted`)\n return existing\n }\n\n // 4. Create the device\n const device = this.createDeviceFromCamera(externalId, cam)\n\n // 5. Add to the in-memory device list (immutable update)\n this.devices = [...this.devices, device]\n\n // 6. Persist adoption\n await this.persistAdoptedDeviceId(externalId)\n\n this.ctx.logger.info(`Adopted camera \"${externalId}\" (total: ${this.devices.length})`)\n return device\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n subscribeLiveEvents(callback: (event: LiveEvent) => void): () => void {\n this.liveEventListeners.add(callback)\n return () => { this.liveEventListeners.delete(callback) }\n }\n\n // --- Private helpers ---\n\n private createDeviceFromCamera(cameraName: string, cam: FrigateCameraConfig): FrigateDevice {\n const streams = buildStreamOptions(cameraName, this.go2rtcStreams)\n const deviceId = `${this.id}/${cameraName}`\n\n const deviceCtx: CamstackContext = {\n id: `device:${deviceId}`,\n logger: this.ctx.logger.child(cameraName),\n eventBus: this.ctx.eventBus,\n storage: this.ctx.storage,\n config: new ElementConfigStore(`device:${deviceId}`, this.ctx.storage),\n }\n\n return new FrigateDevice(\n {\n cameraName,\n providerId: this.id,\n detectWidth: cam.detect?.width ?? 1920,\n detectHeight: cam.detect?.height ?? 1080,\n audioEnabled: cam.audio?.enabled ?? false,\n recordEnabled: cam.record?.enabled ?? false,\n ptzEnabled: false,\n streams,\n frigateHost: this.frigateHost,\n },\n this.api,\n deviceCtx,\n )\n }\n\n private getAdoptedDeviceIds(): readonly string[] {\n return this.ctx.config.get<string[]>(ADOPTED_DEVICES_KEY) ?? []\n }\n\n private async persistAdoptedDeviceId(cameraName: string): Promise<void> {\n const current = this.getAdoptedDeviceIds()\n if (current.includes(cameraName)) return\n await this.ctx.config.set(ADOPTED_DEVICES_KEY, [...current, cameraName])\n }\n}\n\n// --- Helpers ---\n\nfunction buildStreamOptions(\n cameraName: string,\n go2rtcStreams: Record<string, unknown>,\n): StreamOption[] {\n const streams: StreamOption[] = []\n for (const streamName of Object.keys(go2rtcStreams)) {\n if (!streamName.startsWith(cameraName)) continue\n const isSub = streamName.includes('sub') || streamName.includes('ext') || streamName.includes('medium')\n streams.push({ id: streamName, label: streamName, protocol: 'rtsp', quality: isSub ? 'sub' : 'main' })\n }\n return streams\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC0CO,IAAM,mBAAN,MAAuB;AAAA,EAK5B,YAA6B,QAA0B;AAA1B;AAC3B,SAAK,WAAW,OAAO,WAAW,IAAI,QAAQ,QAAQ,EAAE;AAAA,EAC1D;AAAA,EANiB;AAAA,EACT,YAA2B;AAAA,EAC3B,aAA4B;AAAA,EAMpC,MAAc,eAA8B;AAC1C,UAAM,EAAE,UAAU,SAAS,IAAI,KAAK;AAEpC,QAAI,CAAC,YAAY,CAAC,UAAU;AAC1B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,cAAc;AAAA,QACxD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,UAAU,SAAS,CAAC;AAAA,MACnD,CAAC;AAED,UAAI,SAAS,IAAI;AACf,cAAM,UAAU,SAAS,QAAQ,IAAI,YAAY;AACjD,YAAI,SAAS;AACX,eAAK,YAAY;AACjB;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,SAAK,aAAa,SAAS,OAAO,KAAK,GAAG,QAAQ,IAAI,QAAQ,EAAE,EAAE,SAAS,QAAQ,CAAC;AAAA,EACtF;AAAA,EAEQ,eAAuC;AAC7C,UAAM,UAAkC,CAAC;AAEzC,QAAI,KAAK,WAAW;AAClB,cAAQ,QAAQ,IAAI,KAAK;AAAA,IAC3B,WAAW,KAAK,YAAY;AAC1B,cAAQ,eAAe,IAAI,KAAK;AAAA,IAClC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,QAAW,MAAc,SAAmC;AACxE,SAAK,KAAK,OAAO,YAAY,KAAK,OAAO,aAAa,CAAC,KAAK,aAAa,CAAC,KAAK,YAAY;AACzF,YAAM,KAAK,aAAa;AAAA,IAC1B;AAEA,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,GAAG;AAAA,MACH,SAAS;AAAA,QACP,GAAG,KAAK,aAAa;AAAA,QACrB,GAAG,SAAS;AAAA,MACd;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM,IAAI,SAAS,UAAU,QAAQ,IAAI,EAAE;AAAA,IAC5F;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA,EAEA,MAAc,cAAc,MAA+B;AACzD,SAAK,KAAK,OAAO,YAAY,KAAK,OAAO,aAAa,CAAC,KAAK,aAAa,CAAC,KAAK,YAAY;AACzF,YAAM,KAAK,aAAa;AAAA,IAC1B;AAEA,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,SAAS,KAAK,aAAa;AAAA,IAC7B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM,IAAI,SAAS,UAAU,QAAQ,IAAI,EAAE;AAAA,IAC5F;AAEA,UAAM,cAAc,MAAM,SAAS,YAAY;AAC/C,WAAO,OAAO,KAAK,WAAW;AAAA,EAChC;AAAA,EAEA,MAAc,YAAY,MAAc,SAAwC;AAC9E,SAAK,KAAK,OAAO,YAAY,KAAK,OAAO,aAAa,CAAC,KAAK,aAAa,CAAC,KAAK,YAAY;AACzF,YAAM,KAAK,aAAa;AAAA,IAC1B;AAEA,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,GAAG;AAAA,MACH,SAAS;AAAA,QACP,GAAG,KAAK,aAAa;AAAA,QACrB,GAAG,SAAS;AAAA,MACd;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM,IAAI,SAAS,UAAU,QAAQ,IAAI,EAAE;AAAA,IAC5F;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA;AAAA,EAIA,MAAM,YAAoC;AACxC,WAAO,KAAK,QAAuB,aAAa;AAAA,EAClD;AAAA,EAEA,MAAM,mBAAqD;AACzD,WAAO,KAAK,QAAiC,qBAAqB;AAAA,EACpE;AAAA;AAAA,EAIA,MAAM,UAAU,QAAiD;AAC/D,UAAM,QAAQ,iBAAiB,MAAM;AACrC,WAAO,KAAK,QAA2B,cAAc,KAAK,EAAE;AAAA,EAC9D;AAAA,EAEA,MAAM,eAAe,QAAmD;AACtE,UAAM,QAAQ,iBAAiB,MAAM;AACrC,WAAO,KAAK,QAA6B,cAAc,KAAK,EAAE;AAAA,EAChE;AAAA;AAAA,EAIA,MAAM,cACJ,QACA,OACA,QACgC;AAChC,UAAM,QAAQ,iBAAiB,EAAE,OAAO,OAAO,CAAC;AAChD,WAAO,KAAK,QAA+B,QAAQ,mBAAmB,MAAM,CAAC,cAAc,KAAK,EAAE;AAAA,EACpG;AAAA,EAEA,MAAM,kBAAkB,QAAmD;AACzE,UAAM,QAAQ,iBAAiB,MAAM;AACrC,WAAO,KAAK,QAA6B,uBAAuB,KAAK,EAAE;AAAA,EACzE;AAAA;AAAA,EAIA,MAAM,kBAAkB,QAAiC;AACvD,WAAO,KAAK,cAAc,QAAQ,mBAAmB,MAAM,CAAC,aAAa;AAAA,EAC3E;AAAA,EAEA,MAAM,kBAAkB,SAAkC;AACxD,WAAO,KAAK,cAAc,eAAe,mBAAmB,OAAO,CAAC,gBAAgB;AAAA,EACtF;AAAA,EAEA,MAAM,iBAAiB,SAAkC;AACvD,WAAO,KAAK,cAAc,eAAe,mBAAmB,OAAO,CAAC,eAAe;AAAA,EACrF;AAAA,EAEA,MAAM,aAAa,SAAkC;AACnD,WAAO,KAAK,cAAc,eAAe,mBAAmB,OAAO,CAAC,WAAW;AAAA,EACjF;AAAA,EAEA,MAAM,sBAAsB,QAAgB,cAAuC;AACjF,WAAO,KAAK;AAAA,MACV,QAAQ,mBAAmB,MAAM,CAAC,yBAAyB,YAAY;AAAA,IACzE;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,aAAa,YAAoB,UAAmC;AACxE,WAAO,KAAK,YAAY,0BAA0B,mBAAmB,UAAU,CAAC,IAAI;AAAA,MAClF,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,kBAAkB;AAAA,MAC7C,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA,EAIA,MAAM,WACJ,QAC6D;AAC7D,QAAI;AACF,aAAO,MAAM,KAAK;AAAA,QAChB,QAAQ,mBAAmB,MAAM,CAAC;AAAA,MACpC;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,QACA,SACA,QACe;AACf,UAAM,QAAQ,iBAAiB,EAAE,GAAG,QAAQ,QAAQ,CAAC;AACrD,UAAM,KAAK,QAAc,QAAQ,mBAAmB,MAAM,CAAC,OAAO,KAAK,EAAE;AAAA,EAC3E;AAAA;AAAA,EAIA,MAAM,iBAAgD;AACpD,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,UAAU;AACpC,YAAM,cAAc,OAAO,KAAK,OAAO,WAAW,CAAC,CAAC;AACpD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,OAAO;AAAA,QAChB,aAAa,YAAY;AAAA,MAC3B;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AACF;AAIO,SAAS,iBAAiB,QAAkD;AACjF,QAAM,UAAU,OAAO,QAAQ,MAAM,EAAE;AAAA,IACrC,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM,UAAa,MAAM;AAAA,EACtC;AACA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AACA,QAAM,eAAe,IAAI,gBAAgB;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AAClC,iBAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,EACrC;AACA,SAAO,IAAI,aAAa,SAAS,CAAC;AACpC;;;ACvRA,kBAAwB;AAejB,IAAM,oBAAN,MAAwB;AAAA,EAK7B,YACmB,QACA,mBACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAPK,SAA4B;AAAA,EACnB,YAA6C,oBAAI,IAAI;AAAA,EAC9D,aAAa;AAAA,EAOrB,MAAM,UAAyB;AAC7B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,EAAE,WAAW,UAAU,UAAU,YAAY,IAAI,KAAK;AAE5D,WAAK,aAAS,qBAAQ,WAAW;AAAA,QAC/B;AAAA,QACA;AAAA,QACA,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,MAClB,CAAC;AAED,WAAK,OAAO,GAAG,WAAW,MAAM;AAC9B,cAAM,SAAS;AACf,aAAK,OAAQ,UAAU;AAAA,UACrB,GAAG,MAAM;AAAA,UACT,GAAG,MAAM;AAAA,UACT,GAAG,MAAM;AAAA,UACT,GAAG,MAAM;AAAA,QACX,CAAC;AACD,gBAAQ;AAAA,MACV,CAAC;AAED,WAAK,OAAO,GAAG,SAAS,CAAC,QAAQ;AAC/B,eAAO,GAAG;AAAA,MACZ,CAAC;AAED,WAAK,OAAO,GAAG,WAAW,CAAC,OAAe,YAAoB;AAC5D,cAAM,QAAQ;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK;AAAA,QACP;AACA,YAAI,OAAO;AACT,eAAK;AACL,qBAAW,YAAY,KAAK,WAAW;AACrC,qBAAS,KAAK;AAAA,UAChB;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,KAAK,QAAQ;AACf,YAAM,KAAK,OAAO,SAAS;AAC3B,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,UAAU,UAAkD;AAC1D,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM;AACX,WAAK,UAAU,OAAO,QAAQ;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,IAAI,SAAqB;AACvB,WAAO;AAAA,MACL,WAAW,KAAK,QAAQ,aAAa;AAAA,MACrC,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AACF;AA2CO,SAAS,iBACd,OACA,SACA,QACA,aACkB;AAClB,QAAM,aAAa,MAAM,MAAM,GAAG;AAClC,QAAM,cAAc,OAAO,MAAM,GAAG;AACpC,QAAM,WAAW,WAAW,MAAM,YAAY,MAAM;AAGpD,MAAI,SAAS,WAAW,KAAK,SAAS,CAAC,MAAM,UAAU;AACrD,WAAO,oBAAoB,SAAS,WAAW;AAAA,EACjD;AAGA,MAAI,SAAS,WAAW,KAAK,SAAS,CAAC,MAAM,WAAW;AACtD,WAAO,iBAAiB,OAAO;AAAA,EACjC;AAGA,MAAI,SAAS,WAAW,KAAK,SAAS,CAAC,MAAM,YAAY,SAAS,CAAC,MAAM,SAAS;AAChF,UAAM,SAAS,SAAS,CAAC;AACzB,WAAO,iBAAiB,QAAQ,OAAO;AAAA,EACzC;AAGA,MAAI,SAAS,WAAW,KAAK,SAAS,CAAC,MAAM,SAAS;AACpD,UAAM,SAAS,SAAS,CAAC;AACzB,UAAM,SAAS,SAAS,CAAC;AACzB,WAAO,gBAAgB,QAAQ,QAAQ,OAAO;AAAA,EAChD;AAEA,SAAO;AACT;AAEA,SAAS,oBACP,SACA,aACkB;AAClB,MAAI;AACF,UAAM,SAAkC,KAAK,MAAM,QAAQ,SAAS,CAAC;AACrE,UAAM,QAAQ,OAAO,SAAS,OAAO;AACrC,QAAI,CAAC,OAAO,UAAU,CAAC,OAAO,OAAO;AACnC,aAAO;AAAA,IACT;AAEA,UAAM,OAAgC;AAAA,MACpC,WAAW,OAAO,QAAQ;AAAA,MAC1B,IAAI,MAAM;AAAA,MACV,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,OAAO,MAAM;AAAA,MACb,WAAW,MAAM;AAAA,MACjB,SAAS,MAAM;AAAA,MACf,OAAO,MAAM;AAAA,MACb,aAAa,MAAM;AAAA,MACnB,SAAS,MAAM;AAAA,IACjB;AAEA,QAAI,MAAM,KAAK;AACb,YAAM,aAAa,YAAY,IAAI,MAAM,MAAM;AAC/C,UAAI,YAAY;AACd,aAAK,cAAc,qBAAqB,MAAM,KAAK,WAAW,OAAO,WAAW,MAAM;AAAA,MACxF,OAAO;AACL,aAAK,SAAS,MAAM;AAAA,MACtB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,SAAmC;AAC3D,MAAI;AACF,UAAM,SAAmC,KAAK,MAAM,QAAQ,SAAS,CAAC;AACtE,UAAM,QAAQ,OAAO,SAAS,OAAO;AACrC,QAAI,CAAC,OAAO,QAAQ;AAClB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,WAAW,KAAK,IAAI;AAAA,MACpB,MAAM;AAAA,QACJ,WAAW,OAAO,QAAQ;AAAA,QAC1B,IAAI,MAAM;AAAA,QACV,UAAU,MAAM;AAAA,QAChB,WAAW,MAAM;AAAA,QACjB,SAAS,MAAM;AAAA,QACf,SAAS,MAAM,MAAM;AAAA,QACrB,OAAO,MAAM,MAAM;AAAA,QACnB,WAAW,MAAM,MAAM;AAAA,MACzB;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,QAAgB,SAA4B;AACpE,QAAM,QAAQ,QAAQ,SAAS,EAAE,KAAK;AACtC,QAAM,SAAS,UAAU;AAEzB,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,WAAW,KAAK,IAAI;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,EACjB;AACF;AAEA,SAAS,gBAAgB,QAAgB,QAAgB,SAAmC;AAC1F,QAAM,MAAM,QAAQ,SAAS,EAAE,KAAK;AACpC,QAAM,QAAQ,WAAW,GAAG;AAE5B,MAAI,MAAM,KAAK,GAAG;AAChB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,WAAW,KAAK,IAAI;AAAA,IACpB,MAAM,EAAE,QAAQ,MAAM;AAAA,EACxB;AACF;AAOO,SAAS,qBACd,KACA,OACA,QACgD;AAChD,QAAM,CAAC,MAAM,MAAM,MAAM,IAAI,IAAI;AACjC,SAAO;AAAA,IACL,GAAG,OAAO;AAAA,IACV,GAAG,OAAO;AAAA,IACV,IAAI,OAAO,QAAQ;AAAA,IACnB,IAAI,OAAO,QAAQ;AAAA,EACrB;AACF;;;AC9RA,mBAA2B;AAsCpB,IAAM,gBAAN,MAAuC;AAAA,EAW5C,YACmB,QACA,KACjB,KACA;AAHiB;AACA;AAGjB,SAAK,KAAK,GAAG,OAAO,UAAU,IAAI,OAAO,UAAU;AACnD,SAAK,OAAO,OAAO;AACnB,SAAK,aAAa,OAAO;AACzB,SAAK,MAAM;AAGX,UAAM,OAA+B,CAAC,UAAU,UAAU,aAAa,gBAAgB,gBAAgB;AACvG,QAAI,OAAO,aAAc,MAAK,KAAK,eAAe;AAClD,SAAK,eAAe;AAEpB,SAAK,cAAc,IAAI,UAAU,KAAK,aAAa,CAAC;AACpD,SAAK,cAAc,IAAI,gBAAgB,KAAK,mBAAmB,CAAC;AAChE,SAAK,cAAc,IAAI,kBAAkB,KAAK,qBAAqB,CAAC;AACpE,SAAK,cAAc,IAAI,UAAU,KAAK,aAAa,CAAC;AACpD,SAAK,cAAc,IAAI,aAAa,KAAK,gBAAgB,CAAC;AAC1D,QAAI,OAAO,cAAc;AACvB,WAAK,cAAc,IAAI,iBAAiB,KAAK,oBAAoB,CAAC;AAAA,IACpE;AAAA,EACF;AAAA,EAjCS;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAmB,wBAAW;AAAA,EAC9B;AAAA,EAEQ,gBAAgB,oBAAI,IAA6C;AAAA,EAEzE;AAAA,EA2BT,cAA2C,KAAqC;AAC9E,WAAQ,KAAK,cAAc,IAAI,GAAG,KAAW;AAAA,EAC/C;AAAA,EAEA,cAAc,KAAoC;AAChD,WAAO,KAAK,cAAc,IAAI,GAAG;AAAA,EACnC;AAAA,EAEA,WAAwB;AACtB,WAAO,EAAE,QAAQ,KAAK;AAAA,EACxB;AAAA,EAEA,cAA8B;AAC5B,WAAO,EAAE,cAAc,cAAc;AAAA,EACvC;AAAA;AAAA,EAIQ,eAAwB;AAC9B,UAAM,EAAE,KAAK,OAAO,IAAI;AACxB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM,cAAc;AAAE,eAAO,IAAI,kBAAkB,OAAO,UAAU;AAAA,MAAE;AAAA,MACtE,MAAM,mBAA4C;AAChD,eAAO,OAAO,QAAQ,IAAI,QAAM;AAAA,UAC9B,GAAG;AAAA,UACH,KAAK,EAAE,OAAO,UAAU,OAAO,WAAW,SAAS,EAAE,EAAE;AAAA,QACzD,EAAE;AAAA,MACJ;AAAA,MACA,MAAM,aAAa,QAAsB;AACvC,eAAO,0BAA0B,mBAAmB,OAAO,EAAE,CAAC;AAAA,MAChE;AAAA,MACA,oBAAoC;AAAE,eAAO;AAAA,MAAY;AAAA,MACzD,MAAM,oBAAoB;AAAA,MAAC;AAAA,IAC7B;AAAA,EACF;AAAA,EAEQ,qBAAoC;AAC1C,QAAI,eAAe;AACnB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,mBAAmB;AAAE,eAAO;AAAA,MAAa;AAAA,MACzC,YAAY;AAAE,eAAO,MAAM;AAAA,QAAC;AAAA,MAAE;AAAA;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,uBAAwC;AAC9C,UAAM,QAAyB,CAAC;AAChC,UAAM,QAAyB,CAAC;AAEhC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,YAAY;AAAE,eAAO,CAAC;AAAA,MAAE;AAAA,MACxB,eAAe;AAAE,eAAO,MAAM;AAAA,QAAC;AAAA,MAAE;AAAA;AAAA,MAGjC,MAAM,WAAW;AAAE,eAAO;AAAA,MAAM;AAAA,MAChC,MAAM,QAAQ,MAAM;AAAE,cAAM,KAAK,IAAI;AAAA,MAAE;AAAA,MACvC,MAAM,WAAW,IAAI;AAAE,cAAM,IAAI,MAAM,UAAU,OAAK,EAAE,OAAO,EAAE;AAAG,YAAI,KAAK,EAAG,OAAM,OAAO,GAAG,CAAC;AAAA,MAAE;AAAA,MACnG,MAAM,WAAW,IAAI,SAAS;AAAE,cAAM,IAAI,MAAM,KAAK,CAAAA,OAAKA,GAAE,OAAO,EAAE;AAAG,YAAI,EAAG,QAAO,OAAO,GAAG,OAAO;AAAA,MAAE;AAAA;AAAA,MAGzG,MAAM,WAAW;AAAE,eAAO;AAAA,MAAM;AAAA,MAChC,MAAM,QAAQ,MAAM;AAAE,cAAM,KAAK,IAAI;AAAA,MAAE;AAAA,MACvC,MAAM,WAAW,IAAI;AAAE,cAAM,IAAI,MAAM,UAAU,OAAK,EAAE,OAAO,EAAE;AAAG,YAAI,KAAK,EAAG,OAAM,OAAO,GAAG,CAAC;AAAA,MAAE;AAAA;AAAA,MAGnG,sBAAsB;AAAE,eAAO,CAAC;AAAA,MAAE;AAAA,MAClC,oBAAoB;AAAE,eAAO,CAAC;AAAA,MAAE;AAAA,MAChC,0BAA0B;AAAE,eAAO,CAAC;AAAA,MAAE;AAAA,MACtC,sBAAsB;AAAE,eAAO,CAAC;AAAA,MAAE;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,eAAwB;AAC9B,UAAM,EAAE,KAAK,OAAO,IAAI;AACxB,WAAO;AAAA,MACL,MAAM;AAAA,MAEN,MAAM,UAAU,OAA8C;AAC5D,cAAM,MAAM,MAAM,IAAI,UAAU;AAAA,UAC9B,SAAS,OAAO;AAAA,UAChB,OAAO,MAAM;AAAA,UACb,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,UACb,QAAQ,MAAM,kBAAkB,KAAK,GAAG;AAAA,QAC1C,CAAC;AAED,cAAM,SAA8B,IAAI,IAAI,QAAM;AAAA,UAChD,IAAI,EAAE;AAAA,UACN,MAAM;AAAA,UACN,WAAW,EAAE,aAAa;AAAA;AAAA,UAC1B,cAAc,EAAE,WAAW,EAAE,WAAW,MAAO;AAAA,UAC/C,OAAO,EAAE;AAAA,UACT,OAAO,EAAE;AAAA,UACT,SAAS,EAAE;AAAA,UACX,aAAa,EAAE;AAAA,UACf,cAAc,eAAe,EAAE,EAAE;AAAA,QACnC,EAAE;AAEF,eAAO,EAAE,QAAQ,OAAO,OAAO,OAAO;AAAA,MACxC;AAAA,MAEA,MAAM,kBAAkB,SAAiB;AACvC,YAAI;AAAE,iBAAO,MAAM,IAAI,kBAAkB,OAAO;AAAA,QAAE,QAC5C;AAAE,iBAAO;AAAA,QAAK;AAAA,MACtB;AAAA,MAEA,MAAM,iBAAiB,SAAiB;AACtC,YAAI;AAAE,iBAAO,MAAM,IAAI,iBAAiB,OAAO;AAAA,QAAE,QAC3C;AAAE,iBAAO;AAAA,QAAK;AAAA,MACtB;AAAA,MAEA,MAAM,gBAAgB,SAAiB;AACrC,eAAO,eAAe,mBAAmB,OAAO,CAAC;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,kBAA8B;AACpC,UAAM,EAAE,KAAK,OAAO,IAAI;AACxB,WAAO;AAAA,MACL,MAAM;AAAA,MAEN,MAAM,YAAY,OAA+C;AAC/D,cAAM,OAAO,MAAM,IAAI,cAAc,OAAO,YAAY,MAAM,OAAO,MAAM,KAAK;AAChF,eAAO,KAAK,IAAI,QAAM;AAAA,UACpB,IAAI,EAAE,MAAM,GAAG,OAAO,UAAU,IAAI,EAAE,UAAU;AAAA,UAChD,WAAW,EAAE,aAAa;AAAA,UAC1B,SAAS,EAAE,WAAW;AAAA,UACtB,UAAU,EAAE;AAAA,QACd,EAAE;AAAA,MACJ;AAAA,MAEA,MAAM,eAAe,WAAmB,SAAiB;AACvD,cAAM,WAAW,KAAK,MAAM,YAAY,GAAI;AAC5C,cAAM,SAAS,KAAK,MAAM,UAAU,GAAI;AACxC,eAAO,QAAQ,mBAAmB,OAAO,UAAU,CAAC,UAAU,QAAQ,QAAQ,MAAM;AAAA,MACtF;AAAA,MAEA,MAAM,eAAe,aAAqB;AACxC,YAAI;AAAE,iBAAO,MAAM,IAAI,sBAAsB,OAAO,YAAY,KAAK,MAAM,cAAc,GAAI,CAAC;AAAA,QAAE,QAC1F;AAAE,iBAAO;AAAA,QAAK;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,sBAAsC;AAC5C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,gBAAgB;AAAE,eAAO;AAAA,MAAE;AAAA,MAC3B,YAAY;AAAE,eAAO,MAAM;AAAA,QAAC;AAAA,MAAE;AAAA;AAAA,IAChC;AAAA,EACF;AACF;;;AC5NO,IAAM,qBAAN,MAAmD;AAAA,EAKxD,YACmB,WACA,SACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAPK,QAAiC,CAAC;AAAA,EAClC,YAA4D,oBAAI,IAAI;AAAA,EACpE,SAAS;AAAA;AAAA,EAQjB,MAAc,eAA8B;AAC1C,QAAI,KAAK,OAAQ;AACjB,QAAI,CAAC,KAAK,QAAQ,YAAY;AAC5B,WAAK,SAAS;AACd;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,QAAQ,WAAW,MAAM,UAAU;AAAA,QAC5D,OAAO,EAAE,IAAI,KAAK,UAAU;AAAA,QAC5B,OAAO;AAAA,MACT,CAAC;AACD,UAAI,QAAQ,SAAS,GAAG;AACtB,aAAK,QAAS,QAAQ,CAAC,EAAU,QAAQ,CAAC;AAAA,MAC5C;AAAA,IACF,QAAQ;AAAA,IAER;AACA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,SAAkC;AAChC,WAAO,EAAE,GAAG,KAAK,MAAM;AAAA,EACzB;AAAA,EAEA,IAAiB,KAA4B;AAC3C,UAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,QAAI,UAAmB,KAAK;AAC5B,eAAW,QAAQ,OAAO;AACxB,UAAI,WAAW,QAAQ,OAAO,YAAY,SAAU,QAAO;AAC3D,gBAAW,QAAoC,IAAI;AAAA,IACrD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,KAAa,OAA+B;AACpD,UAAM,KAAK,aAAa;AACxB,mBAAe,KAAK,OAAO,KAAK,KAAK;AACrC,UAAM,KAAK,QAAQ;AACnB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO,QAAgD;AAC3D,UAAM,KAAK,aAAa;AACxB,SAAK,QAAQ,EAAE,GAAG,OAAO;AACzB,UAAM,KAAK,QAAQ;AACnB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,SAAS,UAAiE;AACxE,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM;AAAE,WAAK,UAAU,OAAO,QAAQ;AAAA,IAAE;AAAA,EACjD;AAAA;AAAA,EAGA,MAAM,OAAsB;AAC1B,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA;AAAA,EAGA,MAAM,aAAa,UAAkD;AACnE,UAAM,KAAK,aAAa;AACxB,QAAI,OAAO,KAAK,KAAK,KAAK,EAAE,WAAW,GAAG;AACxC,WAAK,QAAQ,EAAE,GAAG,SAAS;AAC3B,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,UAAyB;AACrC,QAAI,CAAC,KAAK,QAAQ,WAAY;AAE9B,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,QAAQ,WAAW,MAAM,UAAU;AAAA,QAC7D,OAAO,EAAE,IAAI,KAAK,UAAU;AAAA,QAC5B,OAAO;AAAA,MACT,CAAC;AAED,UAAI,SAAS,SAAS,GAAG;AACvB,cAAM,KAAK,QAAQ,WAAW,OAAO,UAAU,KAAK,WAAW,KAAK,KAAK;AAAA,MAC3E,OAAO;AACL,cAAM,KAAK,QAAQ,WAAW,OAAO;AAAA,UACnC,YAAY;AAAA,UACZ,IAAI,KAAK;AAAA,UACT,MAAM,KAAK;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,UAAM,WAAW,KAAK,OAAO;AAC7B,eAAW,YAAY,KAAK,WAAW;AACrC,UAAI;AACF,iBAAS,QAAQ;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,eAAe,KAA8B,MAAc,OAAsB;AACxF,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,UAAmC;AACvC,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,EAAE,QAAQ,YAAY,OAAO,QAAQ,IAAI,MAAM,YAAY,QAAQ,IAAI,MAAM,MAAM;AACrF,cAAQ,IAAI,IAAI,CAAC;AAAA,IACnB;AACA,cAAU,QAAQ,IAAI;AAAA,EACxB;AACA,UAAQ,MAAM,MAAM,SAAS,CAAC,CAAE,IAAI;AACtC;;;ACxGA,IAAM,sBAAsB;AAErB,IAAM,kBAAN,MAAiD;AAAA,EAkBtD,YACmB,QACjB,KACA;AAFiB;AAGjB,SAAK,KAAK,OAAO;AACjB,SAAK,OAAO,OAAO;AACnB,SAAK,MAAM;AACX,SAAK,MAAM,IAAI,iBAAiB;AAAA,MAC9B,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,IACnB,CAAC;AACD,SAAK,cAAc,IAAI,IAAI,OAAO,GAAG,EAAE;AAAA,EACzC;AAAA,EA9BS;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,gBAAwB;AAAA,EACxB;AAAA,EAEQ;AAAA,EACT,OAAiC;AAAA,EACjC,UAAoC,CAAC;AAAA,EAC5B,qBAAsD,oBAAI,IAAI;AAAA,EACvE;AAAA;AAAA,EAGA,gBAAsC;AAAA,EACtC,gBAAyC,CAAC;AAAA,EAC1C;AAAA,EAiBR,MAAM,QAAuB;AAC3B,SAAK,IAAI,OAAO,KAAK,8BAA8B,KAAK,OAAO,GAAG,EAAE;AAGpE,SAAK,gBAAgB,MAAM,KAAK,IAAI,UAAU;AAC9C,SAAK,gBAAgB,MAAM,KAAK,IAAI,iBAAiB;AAGrD,UAAM,cAAc,oBAAI,IAA+C;AACvE,eAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,KAAK,cAAc,OAAO,GAAG;AACpE,UAAI,IAAI,QAAQ;AACd,oBAAY,IAAI,MAAM,EAAE,OAAO,IAAI,OAAO,OAAO,QAAQ,IAAI,OAAO,OAAO,CAAC;AAAA,MAC9E;AAAA,IACF;AAGA,UAAM,aAAa,KAAK,oBAAoB;AAC5C,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,UAA2B,CAAC;AAClC,iBAAW,cAAc,YAAY;AACnC,cAAM,MAAM,KAAK,cAAc,QAAQ,UAAU;AACjD,YAAI,CAAC,OAAO,IAAI,YAAY,OAAO;AACjC,eAAK,IAAI,OAAO,KAAK,mBAAmB,UAAU,mDAAmD;AACrG;AAAA,QACF;AACA,gBAAQ,KAAK,KAAK,uBAAuB,YAAY,GAAG,CAAC;AAAA,MAC3D;AACA,WAAK,UAAU;AACf,WAAK,IAAI,OAAO,KAAK,UAAU,QAAQ,MAAM,kBAAkB;AAAA,IACjE,OAAO;AACL,WAAK,IAAI,OAAO,KAAK,uFAAkF;AAAA,IACzG;AAGA,QAAI,KAAK,OAAO,MAAM,WAAW;AAC/B,WAAK,OAAO,IAAI;AAAA,QACd;AAAA,UACE,WAAW,KAAK,OAAO,KAAK;AAAA,UAC5B,UAAU,KAAK,OAAO,KAAK;AAAA,UAC3B,UAAU,KAAK,OAAO,KAAK;AAAA,UAC3B,aAAa,KAAK,OAAO,KAAK,eAAe;AAAA,QAC/C;AAAA,QACA;AAAA,MACF;AAEA,YAAM,KAAK,KAAK,QAAQ;AACxB,WAAK,kBAAkB,KAAK,KAAK,UAAU,CAAC,UAAU;AACpD,mBAAW,YAAY,KAAK,oBAAoB;AAC9C,mBAAS,KAAK;AAAA,QAChB;AAAA,MACF,CAAC;AACD,WAAK,IAAI,OAAO,KAAK,gBAAgB;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,IAAI,OAAO,KAAK,2BAA2B;AAChD,SAAK,kBAAkB;AACvB,SAAK,kBAAkB;AACvB,UAAM,KAAK,MAAM,WAAW;AAC5B,SAAK,OAAO;AACZ,SAAK,UAAU,CAAC;AAChB,SAAK,gBAAgB;AACrB,SAAK,gBAAgB,CAAC;AAAA,EACxB;AAAA,EAEA,YAA4B;AAC1B,WAAO;AAAA,MACL,WAAW,KAAK,MAAM,OAAO,aAAa;AAAA,MAC1C,aAAa,KAAK,QAAQ;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,MAAM,kBAA+C;AAEnD,SAAK,gBAAgB,MAAM,KAAK,IAAI,UAAU;AAC9C,SAAK,gBAAgB,MAAM,KAAK,IAAI,iBAAiB;AAErD,UAAM,aAAa,KAAK,oBAAoB;AAC5C,UAAM,aAAiC,CAAC;AAExC,eAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,KAAK,cAAc,OAAO,GAAG;AACpE,UAAI,IAAI,YAAY,MAAO;AAG3B,YAAM,OAAyC,CAAC,UAAU,UAAU,aAAa,gBAAgB,gBAAgB;AACjH,UAAI,IAAI,OAAO,QAAS,MAAK,KAAK,eAAe;AAEjD,iBAAW,KAAK;AAAA,QACd,YAAY;AAAA,QACZ;AAAA,QACA,MAAM;AAAA,QACN,cAAc;AAAA,QACd,UAAU,EAAE,cAAc,cAAc;AAAA,MAC1C,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,YAAoB,SAAqD;AAEzF,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,gBAAgB,MAAM,KAAK,IAAI,UAAU;AAC9C,WAAK,gBAAgB,MAAM,KAAK,IAAI,iBAAiB;AAAA,IACvD;AAGA,UAAM,MAAM,KAAK,cAAc,QAAQ,UAAU;AACjD,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,WAAW,UAAU,+BAA+B;AAAA,IACtE;AACA,QAAI,IAAI,YAAY,OAAO;AACzB,YAAM,IAAI,MAAM,WAAW,UAAU,iCAAiC;AAAA,IACxE;AAGA,UAAM,WAAW,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AAC/D,QAAI,UAAU;AACZ,WAAK,IAAI,OAAO,KAAK,WAAW,UAAU,sBAAsB;AAChE,aAAO;AAAA,IACT;AAGA,UAAM,SAAS,KAAK,uBAAuB,YAAY,GAAG;AAG1D,SAAK,UAAU,CAAC,GAAG,KAAK,SAAS,MAAM;AAGvC,UAAM,KAAK,uBAAuB,UAAU;AAE5C,SAAK,IAAI,OAAO,KAAK,mBAAmB,UAAU,aAAa,KAAK,QAAQ,MAAM,GAAG;AACrF,WAAO;AAAA,EACT;AAAA,EAEA,aAAwB;AACtB,WAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EACzB;AAAA,EAEA,oBAAoB,UAAkD;AACpE,SAAK,mBAAmB,IAAI,QAAQ;AACpC,WAAO,MAAM;AAAE,WAAK,mBAAmB,OAAO,QAAQ;AAAA,IAAE;AAAA,EAC1D;AAAA;AAAA,EAIQ,uBAAuB,YAAoB,KAAyC;AAC1F,UAAM,UAAU,mBAAmB,YAAY,KAAK,aAAa;AACjE,UAAM,WAAW,GAAG,KAAK,EAAE,IAAI,UAAU;AAEzC,UAAM,YAA6B;AAAA,MACjC,IAAI,UAAU,QAAQ;AAAA,MACtB,QAAQ,KAAK,IAAI,OAAO,MAAM,UAAU;AAAA,MACxC,UAAU,KAAK,IAAI;AAAA,MACnB,SAAS,KAAK,IAAI;AAAA,MAClB,QAAQ,IAAI,mBAAmB,UAAU,QAAQ,IAAI,KAAK,IAAI,OAAO;AAAA,IACvE;AAEA,WAAO,IAAI;AAAA,MACT;AAAA,QACE;AAAA,QACA,YAAY,KAAK;AAAA,QACjB,aAAa,IAAI,QAAQ,SAAS;AAAA,QAClC,cAAc,IAAI,QAAQ,UAAU;AAAA,QACpC,cAAc,IAAI,OAAO,WAAW;AAAA,QACpC,eAAe,IAAI,QAAQ,WAAW;AAAA,QACtC,YAAY;AAAA,QACZ;AAAA,QACA,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,KAAK;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,sBAAyC;AAC/C,WAAO,KAAK,IAAI,OAAO,IAAc,mBAAmB,KAAK,CAAC;AAAA,EAChE;AAAA,EAEA,MAAc,uBAAuB,YAAmC;AACtE,UAAM,UAAU,KAAK,oBAAoB;AACzC,QAAI,QAAQ,SAAS,UAAU,EAAG;AAClC,UAAM,KAAK,IAAI,OAAO,IAAI,qBAAqB,CAAC,GAAG,SAAS,UAAU,CAAC;AAAA,EACzE;AACF;AAIA,SAAS,mBACP,YACA,eACgB;AAChB,QAAM,UAA0B,CAAC;AACjC,aAAW,cAAc,OAAO,KAAK,aAAa,GAAG;AACnD,QAAI,CAAC,WAAW,WAAW,UAAU,EAAG;AACxC,UAAM,QAAQ,WAAW,SAAS,KAAK,KAAK,WAAW,SAAS,KAAK,KAAK,WAAW,SAAS,QAAQ;AACtG,YAAQ,KAAK,EAAE,IAAI,YAAY,OAAO,YAAY,UAAU,QAAQ,SAAS,QAAQ,QAAQ,OAAO,CAAC;AAAA,EACvG;AACA,SAAO;AACT;;;AL7PO,IAAM,uBAAN,MAAoE;AAAA,EAChE,WAA0B;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa;AAAA,IACb,cAAc,CAAC,iBAAiB;AAAA,EAClC;AAAA,EAEQ,WAAmC;AAAA,EAE3C,MAAM,WAAW,SAAsC;AACrD,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAO,KAAK;AACf,cAAQ,OAAO,KAAK,8DAA8D;AAClF;AAAA,IACF;AAEA,UAAM,iBAAwC;AAAA,MAC5C,IAAI,OAAO,MAAM;AAAA,MACjB,MAAM,OAAO,QAAQ;AAAA,MACrB,KAAK,OAAO;AAAA,MACZ,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,IACf;AAEA,SAAK,WAAW,IAAI,gBAAgB,gBAAgB;AAAA,MAClD,IAAI,QAAQ;AAAA,MACZ,QAAQ,QAAQ;AAAA,MAChB,UAAU,QAAQ;AAAA,MAClB,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,YAAQ,OAAO,KAAK,oCAAoC;AAAA,EAC1D;AAAA,EAEA,MAAM,WAA0B;AAC9B,UAAM,KAAK,UAAU,KAAK;AAC1B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,sBACE,MACiC;AACjC,QAAI,SAAS,qBAAqB,KAAK,UAAU;AAC/C,aAAO,KAAK;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,kBAAkC;AAChC,WAAO;AAAA,MACL,UAAU;AAAA,QACR;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,SAAS;AAAA,UACT,QAAQ;AAAA,YACN,EAAE,MAAM,QAAQ,KAAK,OAAO,OAAO,eAAe,UAAU,MAAM,aAAa,sBAAsB;AAAA,YACrG,EAAE,MAAM,QAAQ,KAAK,QAAQ,OAAO,gBAAgB,aAAa,cAAc;AAAA,YAC/E,EAAE,MAAM,QAAQ,KAAK,YAAY,OAAO,WAAW;AAAA,YACnD,EAAE,MAAM,YAAY,KAAK,YAAY,OAAO,YAAY,YAAY,KAAK;AAAA,UAC3E;AAAA,QACF;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO;AAAA,UACP,kBAAkB;AAAA,UAClB,SAAS;AAAA,UACT,QAAQ;AAAA,YACN,EAAE,MAAM,QAAQ,KAAK,kBAAkB,OAAO,mBAAmB,aAAa,qBAAqB;AAAA,YACnG,EAAE,MAAM,QAAQ,KAAK,oBAAoB,OAAO,gBAAgB,aAAa,UAAU;AAAA,YACvF,EAAE,MAAM,QAAQ,KAAK,iBAAiB,OAAO,gBAAgB;AAAA,YAC7D,EAAE,MAAM,YAAY,KAAK,iBAAiB,OAAO,iBAAiB,YAAY,KAAK;AAAA,UACrF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAqC;AACnC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAM,eAAe,SAAiD;AAAA,EAEtE;AACF;","names":["z"]}
package/dist/addon.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ FrigateProviderAddon
3
+ } from "./chunk-4YBCO3RD.mjs";
4
+ export {
5
+ FrigateProviderAddon
6
+ };
7
+ //# sourceMappingURL=addon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}