@happyvertical/smrt-analytics 0.55.17 → 0.55.19

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["AnalyticsProvider","AnalyticsPropertyStatus","DataStreamType","DataStreamStatus","ReportStatus","ReportFrequency","CustomDimensionScope","TrackingEventStatus","CountingMethod"],"sources":["../src/__smrt-register__.ts","../src/prompts.ts","../src/types/index.ts","../src/models/AnalyticsDataStream.ts","../src/collections/AnalyticsDataStreamCollection.ts","../src/models/AnalyticsEvent.ts","../src/collections/AnalyticsEventCollection.ts","../src/models/AnalyticsProperty.ts","../src/collections/AnalyticsPropertyCollection.ts","../src/models/AnalyticsReport.ts","../src/collections/AnalyticsReportCollection.ts","../src/recipes.ts"],"sourcesContent":["// Generated by @happyvertical/smrt-core smrtPlugin at library build time.\n// The package manifest is inlined so self-registration cannot miss when a\n// downstream bundler relocates this module away from dist/manifest.json\n// (https://github.com/happyvertical/smrt/issues/1506,\n// https://github.com/happyvertical/smrt/issues/1507).\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(JSON.parse(\"{\\\"version\\\":\\\"1.0.0\\\",\\\"timestamp\\\":0,\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"packageVersion\\\":\\\"0.55.17\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-analytics:AnalyticsDataStreamCollection\\\":{\\\"name\\\":\\\"analyticsdatastreamcollection\\\",\\\"className\\\":\\\"AnalyticsDataStreamCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsDataStreamCollection\\\",\\\"collection\\\":\\\"analyticsdatastreams\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsDataStreamCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProperty\\\":{\\\"name\\\":\\\"findByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByExternalId\\\":{\\\"name\\\":\\\"findByExternalId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"externalId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByMeasurementId\\\":{\\\"name\\\":\\\"findByMeasurementId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"measurementId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByType\\\":{\\\"name\\\":\\\"findByType\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"streamType\\\",\\\"type\\\":\\\"DataStreamType\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findWebStreams\\\":{\\\"name\\\":\\\"findWebStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findIOSStreams\\\":{\\\"name\\\":\\\"findIOSStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findAndroidStreams\\\":{\\\"name\\\":\\\"findAndroidStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findMobileStreams\\\":{\\\"name\\\":\\\"findMobileStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"DataStreamStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActive\\\":{\\\"name\\\":\\\"findActive\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActiveByProperty\\\":{\\\"name\\\":\\\"findActiveByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsDataStream\\\",\\\"exportName\\\":\\\"AnalyticsDataStreamCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsDataStreamCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_data_streams\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_data_streams\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"stream_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"firebase_app_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"default_uri\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"bundle_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"package_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"enhanced_measurement\\\\\\\" BOOLEAN DEFAULT TRUE\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"stream_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"firebase_app_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"default_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"bundle_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"package_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"enhanced_measurement\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":true}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_data_streams_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_data_streams_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_data_streams_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_data_streams_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"851609ba\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsEventCollection\\\":{\\\"name\\\":\\\"analyticseventcollection\\\",\\\"className\\\":\\\"AnalyticsEventCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsEventCollection\\\",\\\"collection\\\":\\\"analyticsevents\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsEventCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProperty\\\":{\\\"name\\\":\\\"findByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByEventName\\\":{\\\"name\\\":\\\"findByEventName\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"eventName\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByClientId\\\":{\\\"name\\\":\\\"findByClientId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"clientId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByUserId\\\":{\\\"name\\\":\\\"findByUserId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"userId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"TrackingEventStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPending\\\":{\\\"name\\\":\\\"findPending\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findSent\\\":{\\\"name\\\":\\\"findSent\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findFailed\\\":{\\\"name\\\":\\\"findFailed\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findForRetry\\\":{\\\"name\\\":\\\"findForRetry\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"maxRetries\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPendingByProperty\\\":{\\\"name\\\":\\\"findPendingByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByDateRange\\\":{\\\"name\\\":\\\"findByDateRange\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"startDate\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"endDate\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findConversions\\\":{\\\"name\\\":\\\"findConversions\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPageviews\\\":{\\\"name\\\":\\\"findPageviews\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"countByEventName\\\":{\\\"name\\\":\\\"countByEventName\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Map<string, number>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPropertyStats\\\":{\\\"name\\\":\\\"getPropertyStats\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPropertyStatsWithTrend\\\":{\\\"name\\\":\\\"getPropertyStatsWithTrend\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"now\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"timeZone\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true,\\\"default\\\":\\\"UTC\\\"}],\\\"returnType\\\":\\\"Promise<PropertyStatsWithTrend>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBatchPropertyStats\\\":{\\\"name\\\":\\\"getBatchPropertyStats\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"now\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"timeZone\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true,\\\"default\\\":\\\"UTC\\\"}],\\\"returnType\\\":\\\"Promise<Map<string, PropertyStatsWithTrend>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsEvent\\\",\\\"exportName\\\":\\\"AnalyticsEventCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsEventCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_events\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_events\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"event_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"client_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"params\\\\\\\" TEXT DEFAULT '{}',\\\\n \\\\\\\"event_timestamp\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"sent_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"error_message\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"retry_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"non_personalized_ads\\\\\\\" BOOLEAN DEFAULT FALSE,\\\\n \\\\\\\"session_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_path\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_title\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_agent\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"ip_address\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"event_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"client_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"params\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"},\\\"event_timestamp\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"sent_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"error_message\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"retry_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"non_personalized_ads\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":false},\\\"session_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_path\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_title\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_agent\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"ip_address\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_events_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_events_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_events_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_events_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"bee09e1d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsPropertyCollection\\\":{\\\"name\\\":\\\"analyticspropertycollection\\\",\\\"className\\\":\\\"AnalyticsPropertyCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsPropertyCollection\\\",\\\"collection\\\":\\\"analyticsproperties\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsPropertyCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByExternalId\\\":{\\\"name\\\":\\\"findByExternalId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"externalId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByMeasurementId\\\":{\\\"name\\\":\\\"findByMeasurementId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"measurementId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findBySiteDomain\\\":{\\\"name\\\":\\\"findBySiteDomain\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"siteDomain\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"AnalyticsProvider\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByProvider\\\":{\\\"name\\\":\\\"findByProvider\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"AnalyticsProvider\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findGA4Properties\\\":{\\\"name\\\":\\\"findGA4Properties\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPlausibleSites\\\":{\\\"name\\\":\\\"findPlausibleSites\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findMatomoSites\\\":{\\\"name\\\":\\\"findMatomoSites\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"AnalyticsPropertyStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActive\\\":{\\\"name\\\":\\\"findActive\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findNeedingSync\\\":{\\\"name\\\":\\\"findNeedingSync\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"hoursAgo\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsProperty\\\",\\\"exportName\\\":\\\"AnalyticsPropertyCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsPropertyCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_properties\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_properties\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"provider\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"api_secret\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"site_domain\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"time_zone\\\\\\\" TEXT DEFAULT 'America/Los_Angeles',\\\\n \\\\\\\"currency_code\\\\\\\" TEXT DEFAULT 'USD',\\\\n \\\\\\\"industry_category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"service_level\\\\\\\" TEXT DEFAULT 'STANDARD',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"last_sync_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"provider_metadata\\\\\\\" TEXT DEFAULT '{}'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"provider\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"api_secret\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"site_domain\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"time_zone\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"America/Los_Angeles\\\"},\\\"currency_code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"USD\\\"},\\\"industry_category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"service_level\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"STANDARD\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_sync_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"provider_metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_properties_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_properties_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_properties_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"f38a20b9\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsReportCollection\\\":{\\\"name\\\":\\\"analyticsreportcollection\\\",\\\"className\\\":\\\"AnalyticsReportCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsReportCollection\\\",\\\"collection\\\":\\\"analyticsreports\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsReportCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProperty\\\":{\\\"name\\\":\\\"findByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"ReportStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findDrafts\\\":{\\\"name\\\":\\\"findDrafts\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findScheduled\\\":{\\\"name\\\":\\\"findScheduled\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findCompleted\\\":{\\\"name\\\":\\\"findCompleted\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findFailed\\\":{\\\"name\\\":\\\"findFailed\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByFrequency\\\":{\\\"name\\\":\\\"findByFrequency\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"frequency\\\",\\\"type\\\":\\\"ReportFrequency\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findRecurring\\\":{\\\"name\\\":\\\"findRecurring\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findDue\\\":{\\\"name\\\":\\\"findDue\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByPropertyAndStatus\\\":{\\\"name\\\":\\\"findByPropertyAndStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"ReportStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findRecentlyRun\\\":{\\\"name\\\":\\\"findRecentlyRun\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"hoursAgo\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsReport\\\",\\\"exportName\\\":\\\"AnalyticsReportCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsReportCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_reports\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_reports\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"description\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"dimensions\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"metrics\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"date_range_start\\\\\\\" TEXT DEFAULT '7daysAgo',\\\\n \\\\\\\"date_range_end\\\\\\\" TEXT DEFAULT 'today',\\\\n \\\\\\\"dimension_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"metric_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"order_by\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"max_results\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"frequency\\\\\\\" TEXT,\\\\n \\\\\\\"last_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"next_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"result_data\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"row_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"last_error\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"dimensions\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"metrics\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"date_range_start\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"7daysAgo\\\"},\\\"date_range_end\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"today\\\"},\\\"dimension_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"metric_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"order_by\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"max_results\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"frequency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"next_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"result_data\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"row_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"last_error\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_reports_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_reports_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_reports_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_reports_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"22ce0f1c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsDataStream\\\":{\\\"name\\\":\\\"analyticsdatastream\\\",\\\"className\\\":\\\"AnalyticsDataStream\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsDataStream\\\",\\\"collection\\\":\\\"analyticsdatastreams\\\",\\\"description\\\":\\\"AnalyticsDataStream represents a data stream (web, iOS, Android) for an analytics property.\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsDataStream.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"propertyId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"AnalyticsProperty\\\"},\\\"displayName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"streamType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"enum\\\":[\\\"WEB_DATA_STREAM\\\",\\\"ANDROID_APP_DATA_STREAM\\\",\\\"IOS_APP_DATA_STREAM\\\"]},\\\"externalId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurementId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"firebaseAppId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"defaultUri\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"bundleId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"packageName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"enum\\\":[\\\"active\\\",\\\"inactive\\\"]},\\\"enhancedMeasurement\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":false,\\\"default\\\":true}},\\\"methods\\\":{\\\"isWeb\\\":{\\\"name\\\":\\\"isWeb\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isIOS\\\":{\\\"name\\\":\\\"isIOS\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isAndroid\\\":{\\\"name\\\":\\\"isAndroid\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isMobileApp\\\":{\\\"name\\\":\\\"isMobileApp\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPlatformId\\\":{\\\"name\\\":\\\"getPlatformId\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"string\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsDataStream\\\",\\\"collectionExportName\\\":\\\"AnalyticsDataStreamCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_data_streams\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_data_streams\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"stream_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"firebase_app_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"default_uri\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"bundle_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"package_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"enhanced_measurement\\\\\\\" BOOLEAN DEFAULT TRUE\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"stream_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"firebase_app_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"default_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"bundle_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"package_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"enhanced_measurement\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":true}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_data_streams_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_data_streams_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_data_streams_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_data_streams_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"851609ba\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsEvent\\\":{\\\"name\\\":\\\"analyticsevent\\\",\\\"className\\\":\\\"AnalyticsEvent\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsEvent\\\",\\\"collection\\\":\\\"analyticsevents\\\",\\\"description\\\":\\\"AnalyticsEvent represents a tracked analytics event (server-side tracking log).\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsEvent.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"propertyId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"AnalyticsProperty\\\"},\\\"eventName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"clientId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"userId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"params\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"{}\\\"},\\\"eventTimestamp\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"enum\\\":[\\\"pending\\\",\\\"sent\\\",\\\"failed\\\"]},\\\"sentAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"errorMessage\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"retryCount\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"nonPersonalizedAds\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":false,\\\"default\\\":false},\\\"sessionId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"pagePath\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"pageTitle\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"userAgent\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"ipAddress\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{\\\"getParams\\\":{\\\"name\\\":\\\"getParams\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, string | number | boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setParams\\\":{\\\"name\\\":\\\"setParams\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"params\\\",\\\"type\\\":\\\"Record<string, string | number | boolean>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"addParam\\\":{\\\"name\\\":\\\"addParam\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"key\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"value\\\",\\\"type\\\":\\\"string | number | boolean\\\",\\\"optional\\\":false,\\\"unionBranches\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"number\\\"},{\\\"type\\\":\\\"boolean\\\"}]}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isPageview\\\":{\\\"name\\\":\\\"isPageview\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isConversion\\\":{\\\"name\\\":\\\"isConversion\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markSent\\\":{\\\"name\\\":\\\"markSent\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markFailed\\\":{\\\"name\\\":\\\"markFailed\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"error\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"resetForRetry\\\":{\\\"name\\\":\\\"resetForRetry\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"shouldRetry\\\":{\\\"name\\\":\\\"shouldRetry\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"maxRetries\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"toTrackEvent\\\":{\\\"name\\\":\\\"toTrackEvent\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"object\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"track\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsEvent\\\",\\\"collectionExportName\\\":\\\"AnalyticsEventCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_events\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_events\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"event_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"client_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"params\\\\\\\" TEXT DEFAULT '{}',\\\\n \\\\\\\"event_timestamp\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"sent_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"error_message\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"retry_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"non_personalized_ads\\\\\\\" BOOLEAN DEFAULT FALSE,\\\\n \\\\\\\"session_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_path\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_title\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_agent\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"ip_address\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"event_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"client_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"params\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"},\\\"event_timestamp\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"sent_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"error_message\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"retry_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"non_personalized_ads\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":false},\\\"session_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_path\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_title\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_agent\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"ip_address\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_events_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_events_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_events_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_events_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"bee09e1d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\":{\\\"name\\\":\\\"analyticsproperty\\\",\\\"className\\\":\\\"AnalyticsProperty\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\",\\\"collection\\\":\\\"analyticsproperties\\\",\\\"description\\\":\\\"AnalyticsProperty represents an analytics property (GA4 property, Plausible site, or Matomo site).\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsProperty.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"displayName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The name people see for this website or app.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name people see for this website or app.\\\"}},\\\"provider\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Which analytics service tracks it: Google Analytics 4, Plausible or Matomo.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Which analytics service tracks it: Google Analytics 4, Plausible or Matomo.\\\"},\\\"enum\\\":[\\\"ga4\\\",\\\"plausible\\\",\\\"matomo\\\"]},\\\"externalId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The id the analytics service gives the property, such as properties/123456789 or a Matomo site id.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The id the analytics service gives the property, such as properties/123456789 or a Matomo site id.\\\"}},\\\"measurementId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The tracking id for a web stream, such as G-XXXXXXXXXX.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The tracking id for a web stream, such as G-XXXXXXXXXX.\\\"}},\\\"apiSecret\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The secret key your analytics service issues for this property. It is kept out of responses and tool results, but it is not encrypted in the database.\\\",\\\"_meta\\\":{\\\"sensitive\\\":true,\\\"description\\\":\\\"The secret key your analytics service issues for this property. It is kept out of responses and tool results, but it is not encrypted in the database.\\\"},\\\"sensitive\\\":true},\\\"siteDomain\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The website address, used by Plausible and Matomo.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The website address, used by Plausible and Matomo.\\\"}},\\\"timeZone\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"America/Los_Angeles\\\",\\\"description\\\":\\\"The time zone the figures are reported in.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The time zone the figures are reported in.\\\"}},\\\"currencyCode\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"USD\\\",\\\"description\\\":\\\"The currency used for revenue figures, such as USD.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The currency used for revenue figures, such as USD.\\\"}},\\\"industryCategory\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"serviceLevel\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"STANDARD\\\"},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Whether tracking is active, inactive or still being set up.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Whether tracking is active, inactive or still being set up.\\\"},\\\"enum\\\":[\\\"active\\\",\\\"inactive\\\",\\\"pending\\\"]},\\\"lastSyncAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false,\\\"description\\\":\\\"When the property was last synced with the analytics service.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"When the property was last synced with the analytics service.\\\"}},\\\"providerMetadata\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"{}\\\",\\\"description\\\":\\\"Extra connection details from your analytics service. They are kept out of responses and tool results, but they are not encrypted in the database.\\\",\\\"_meta\\\":{\\\"sensitive\\\":true,\\\"description\\\":\\\"Extra connection details from your analytics service. They are kept out of responses and tool results, but they are not encrypted in the database.\\\"},\\\"sensitive\\\":true}},\\\"methods\\\":{\\\"isGA4\\\":{\\\"name\\\":\\\"isGA4\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isPlausible\\\":{\\\"name\\\":\\\"isPlausible\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isMatomo\\\":{\\\"name\\\":\\\"isMatomo\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getProviderMetadata\\\":{\\\"name\\\":\\\"getProviderMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setProviderMetadata\\\":{\\\"name\\\":\\\"setProviderMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metadata\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markSynced\\\":{\\\"name\\\":\\\"markSynced\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"analyzePerformance\\\":{\\\"name\\\":\\\"analyzePerformance\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"object\\\",\\\"optional\\\":true,\\\"memberTypes\\\":[\\\"string\\\"]}],\\\"returnType\\\":\\\"Promise<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isPerformingWell\\\":{\\\"name\\\":\\\"isPerformingWell\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"sync\\\",\\\"runReport\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsProperty\\\",\\\"collectionExportName\\\":\\\"AnalyticsPropertyCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_properties\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_properties\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"provider\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"api_secret\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"site_domain\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"time_zone\\\\\\\" TEXT DEFAULT 'America/Los_Angeles',\\\\n \\\\\\\"currency_code\\\\\\\" TEXT DEFAULT 'USD',\\\\n \\\\\\\"industry_category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"service_level\\\\\\\" TEXT DEFAULT 'STANDARD',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"last_sync_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"provider_metadata\\\\\\\" TEXT DEFAULT '{}'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"provider\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"api_secret\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"site_domain\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"time_zone\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"America/Los_Angeles\\\"},\\\"currency_code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"USD\\\"},\\\"industry_category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"service_level\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"STANDARD\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_sync_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"provider_metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_properties_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_properties_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_properties_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"f38a20b9\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\":{\\\"name\\\":\\\"analyticsreport\\\",\\\"className\\\":\\\"AnalyticsReport\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\",\\\"collection\\\":\\\"analyticsreports\\\",\\\"description\\\":\\\"AnalyticsReport represents a saved report configuration with optional scheduling.\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsReport.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"propertyId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"AnalyticsProperty\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The website or app this report is about.\\\"}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"A short name for the report, such as Weekly traffic.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A short name for the report, such as Weekly traffic.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"What the report is for.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"What the report is for.\\\"}},\\\"dimensions\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"[]\\\",\\\"description\\\":\\\"What to break the figures down by, such as country or device, as a list.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"What to break the figures down by, such as country or device, as a list.\\\"}},\\\"metrics\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"[]\\\",\\\"description\\\":\\\"What to count, such as active users or sessions, as a list.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"What to count, such as active users or sessions, as a list.\\\"}},\\\"dateRangeStart\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"7daysAgo\\\",\\\"description\\\":\\\"Where the period begins: a date, or a relative value such as 7daysAgo.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the period begins: a date, or a relative value such as 7daysAgo.\\\"}},\\\"dateRangeEnd\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"today\\\",\\\"description\\\":\\\"Where the period ends: a date, or a relative value such as today.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the period ends: a date, or a relative value such as today.\\\"}},\\\"dimensionFilter\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"metricFilter\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"orderBy\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"maxResults\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Where the report stands: draft, scheduled, running, completed or failed.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the report stands: draft, scheduled, running, completed or failed.\\\"},\\\"enum\\\":[\\\"draft\\\",\\\"scheduled\\\",\\\"running\\\",\\\"completed\\\",\\\"failed\\\"]},\\\"frequency\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"How often the report repeats: once, daily, weekly or monthly.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"How often the report repeats: once, daily, weekly or monthly.\\\"},\\\"enum\\\":[\\\"once\\\",\\\"daily\\\",\\\"weekly\\\",\\\"monthly\\\"]},\\\"lastRunAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false,\\\"description\\\":\\\"When the report last ran.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"When the report last ran.\\\"}},\\\"nextRunAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false,\\\"description\\\":\\\"When the report is next due to run.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"When the report is next due to run.\\\"}},\\\"resultData\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"rowCount\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0,\\\"description\\\":\\\"How many rows the last run returned.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"How many rows the last run returned.\\\"}},\\\"lastError\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"Why the last run failed. Empty when it worked.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Why the last run failed. Empty when it worked.\\\"}}},\\\"methods\\\":{\\\"getDimensions\\\":{\\\"name\\\":\\\"getDimensions\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Array<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setDimensions\\\":{\\\"name\\\":\\\"setDimensions\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"dimensions\\\",\\\"type\\\":\\\"Array<object>\\\",\\\"optional\\\":false,\\\"memberTypes\\\":[\\\"string\\\"]}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getMetrics\\\":{\\\"name\\\":\\\"getMetrics\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Array<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setMetrics\\\":{\\\"name\\\":\\\"setMetrics\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metrics\\\",\\\"type\\\":\\\"Array<object>\\\",\\\"optional\\\":false,\\\"memberTypes\\\":[\\\"string\\\"]}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getResultData\\\":{\\\"name\\\":\\\"getResultData\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown> | null\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setResultData\\\":{\\\"name\\\":\\\"setResultData\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"data\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markRunning\\\":{\\\"name\\\":\\\"markRunning\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markCompleted\\\":{\\\"name\\\":\\\"markCompleted\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"rowCount\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markFailed\\\":{\\\"name\\\":\\\"markFailed\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"error\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"calculateNextRun\\\":{\\\"name\\\":\\\"calculateNextRun\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isDue\\\":{\\\"name\\\":\\\"isDue\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"analyzeResults\\\":{\\\"name\\\":\\\"analyzeResults\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_options\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"hasPositiveTrends\\\":{\\\"name\\\":\\\"hasPositiveTrends\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsReport\\\",\\\"collectionExportName\\\":\\\"AnalyticsReportCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_reports\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_reports\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"description\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"dimensions\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"metrics\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"date_range_start\\\\\\\" TEXT DEFAULT '7daysAgo',\\\\n \\\\\\\"date_range_end\\\\\\\" TEXT DEFAULT 'today',\\\\n \\\\\\\"dimension_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"metric_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"order_by\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"max_results\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"frequency\\\\\\\" TEXT,\\\\n \\\\\\\"last_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"next_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"result_data\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"row_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"last_error\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"dimensions\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"metrics\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"date_range_start\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"7daysAgo\\\"},\\\"date_range_end\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"today\\\"},\\\"dimension_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"metric_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"order_by\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"max_results\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"frequency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"next_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"result_data\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"row_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"last_error\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_reports_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_reports_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_reports_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_reports_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"22ce0f1c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"recipes\\\":[{\\\"id\\\":\\\"analytics.reports\\\",\\\"className\\\":\\\"AnalyticsReportsRecipe\\\",\\\"label\\\":\\\"Analytics\\\",\\\"summary\\\":\\\"Save traffic reports for your websites and apps and get a plain-language read on the results.\\\",\\\"synonyms\\\":[\\\"analytics\\\",\\\"traffic\\\",\\\"pageviews\\\",\\\"visitors\\\",\\\"web stats\\\",\\\"google analytics\\\",\\\"plausible\\\",\\\"matomo\\\"],\\\"models\\\":[\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\",\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\"],\\\"nav\\\":[{\\\"label\\\":\\\"Analytics properties\\\",\\\"model\\\":\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\",\\\"icon\\\":\\\"home\\\",\\\"description\\\":\\\"The websites and apps you track, and who tracks them.\\\"},{\\\"label\\\":\\\"Analytics reports\\\",\\\"model\\\":\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\",\\\"icon\\\":\\\"fileText\\\",\\\"description\\\":\\\"Saved reports, how often they run and what they found.\\\"}],\\\"requires\\\":[],\\\"section\\\":{\\\"id\\\":\\\"analytics\\\",\\\"label\\\":\\\"Analytics\\\",\\\"icon\\\":\\\"layers\\\",\\\"description\\\":\\\"How your websites and apps are doing.\\\"},\\\"options\\\":{\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\":{\\\"fields\\\":{\\\"name\\\":{\\\"visibility\\\":\\\"hidden\\\"},\\\"industryCategory\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"serviceLevel\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"lastSyncAt\\\":{\\\"locked\\\":true}}},\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\":{\\\"fields\\\":{\\\"dimensionFilter\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"metricFilter\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"orderBy\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"maxResults\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"resultData\\\":{\\\"visibility\\\":\\\"hidden\\\",\\\"locked\\\":true},\\\"rowCount\\\":{\\\"locked\\\":true},\\\"lastRunAt\\\":{\\\"locked\\\":true},\\\"nextRunAt\\\":{\\\"locked\\\":true},\\\"lastError\\\":{\\\"locked\\\":true}}}},\\\"help\\\":{\\\"markdown\\\":\\\"## Overview\\\\n\\\\nAnalytics keeps the traffic reports you care about for your websites and apps.\\\\nAdd a property for each site or app you track in Google Analytics 4, Plausible\\\\nor Matomo. Save a report on it that says what to count, what to break the\\\\nfigures down by, and over which dates. The rows each run returns are stored\\\\nwith the report, and the assistant can read them back to you in plain words.\\\\n\\\\nProperties and reports are kept on your server. Provider secrets are stored on\\\\nthe property and never sent to the assistant or returned in responses, but they\\\\nare not encrypted in the database.\\\\n\\\\n## Tasks\\\\n\\\\n### Add a property\\\\n\\\\n1. Create a new property.\\\\n2. Enter the **{field:AnalyticsProperty.displayName}** and pick the\\\\n **{field:AnalyticsProperty.provider}**.\\\\n3. Enter the **{field:AnalyticsProperty.externalId}** the analytics service\\\\n gives it.\\\\n4. For Google Analytics, enter the **{field:AnalyticsProperty.measurementId}**.\\\\n For Plausible or Matomo, enter the **{field:AnalyticsProperty.siteDomain}**.\\\\n5. Keep **{field:AnalyticsProperty.status}** on active while you want it\\\\n tracked.\\\\n\\\\n### Save a report\\\\n\\\\n1. Create a new report and choose the property in\\\\n **{field:AnalyticsReport.propertyId}**.\\\\n2. Give it a **{field:AnalyticsReport.name}**.\\\\n3. List what to break the figures down by in\\\\n **{field:AnalyticsReport.dimensions}** and what to count in\\\\n **{field:AnalyticsReport.metrics}**.\\\\n4. Set the period with **{field:AnalyticsReport.dateRangeStart}** and\\\\n **{field:AnalyticsReport.dateRangeEnd}**.\\\\n5. Pick **{field:AnalyticsReport.frequency}** to repeat it daily, weekly or\\\\n monthly.\\\\n\\\\n### Check what a report found\\\\n\\\\n1. **{field:AnalyticsReport.status}** says where the report stands, and\\\\n **{field:AnalyticsReport.lastRunAt}** when it last ran.\\\\n2. **{field:AnalyticsReport.rowCount}** shows how many rows came back.\\\\n3. If a run failed, **{field:AnalyticsReport.lastError}** says why.\\\\n\\\\n### Ask the assistant\\\\n\\\\n1. Ask about a saved report by name, for example \\\\\\\"how did last week's traffic\\\\n report look?\\\\\\\". When your app has the analytics tools switched on, the\\\\n assistant can list your saved reports and open one to read its figures.\\\\n2. A written summary of a report comes from your app, which sends the stored\\\\n rows to the AI provider as they are. Leave out dimensions that identify a\\\\n person.\\\\n3. For totals your own app calculates, such as orders or signups, ask for a\\\\n refreshed report. The assistant reads those with `reports.query`; see\\\\n Reports.\\\\n4. For a one-off question those reports do not answer, the assistant can\\\\n propose a report with `reports.runtime.define`. Nothing is saved until you\\\\n confirm it.\\\\n\\\",\\\"fieldRefs\\\":[\\\"AnalyticsProperty.displayName\\\",\\\"AnalyticsProperty.externalId\\\",\\\"AnalyticsProperty.measurementId\\\",\\\"AnalyticsProperty.provider\\\",\\\"AnalyticsProperty.siteDomain\\\",\\\"AnalyticsProperty.status\\\",\\\"AnalyticsReport.dateRangeEnd\\\",\\\"AnalyticsReport.dateRangeStart\\\",\\\"AnalyticsReport.dimensions\\\",\\\"AnalyticsReport.frequency\\\",\\\"AnalyticsReport.lastError\\\",\\\"AnalyticsReport.lastRunAt\\\",\\\"AnalyticsReport.metrics\\\",\\\"AnalyticsReport.name\\\",\\\"AnalyticsReport.propertyId\\\",\\\"AnalyticsReport.rowCount\\\",\\\"AnalyticsReport.status\\\"]},\\\"surfaces\\\":[{\\\"kind\\\":\\\"route\\\",\\\"path\\\":\\\"/analytics/summary\\\",\\\"export\\\":\\\"@happyvertical/smrt-analytics/svelte#AnalyticsSummary\\\",\\\"label\\\":\\\"Traffic summary\\\"}],\\\"runtime\\\":\\\"server\\\",\\\"demo\\\":{\\\"mode\\\":\\\"server\\\",\\\"reasons\\\":[\\\"The recipe declares runtime \\\\\\\"server\\\\\\\".\\\",\\\"Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624).\\\"]}}],\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\",\\\"@happyvertical/smrt-prompts\\\",\\\"@happyvertical/smrt-tenancy\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3624\\\"],\\\"via\\\":[\\\"@happyvertical/smrt-tenancy\\\"]},\\\"stampsConstructors\\\":true}\"));\n","/**\n * Prompt registrations for the @happyvertical/smrt-analytics package.\n *\n * Prompts are registered at module-load time via `definePrompt()` so that\n * tenant-aware overrides can be applied at call time via `resolvePrompt()`.\n *\n * Mirrors the pattern used by `@happyvertical/smrt-properties` (see\n * `prompts.ts`) and `@happyvertical/smrt-content` (see `content-prompts.ts`).\n *\n * PII / internal-field exclusion policy:\n *\n * Variables that this package never forwards to the AI provider:\n * - `id`, `tenantId`, `propertyId` and other foreign-key/UUID fields —\n * they identify internal records and provide no analytic value.\n * - `apiSecret`, `measurementId`, `externalId`, `siteDomain` — these are\n * provider credentials or platform-specific identifiers (e.g. GA4 API\n * secrets, Matomo `idSite`, custom `G-XXXX` measurement IDs) that may\n * be tenant-private configuration.\n * - `providerMetadata` — extensible JSON blob that may contain\n * credentials, account IDs, or other configuration secrets.\n * - `lastError`, raw `dimensionFilter` / `metricFilter` JSON — internal\n * error strings (which may contain auth tokens) and filter expressions\n * that may reference cookie IDs or user-pseudo-IDs are kept out of the\n * prompt variable set.\n *\n * Variables this package DOES forward (potentially carrying PII the caller\n * persisted):\n * - `reportData` (a JSON.stringify of the persisted `resultData`) — this\n * is forwarded VERBATIM to the AI in `analyzeResults` and\n * `hasPositiveTrends`. The package cannot strip PII because the row\n * schema is determined by which dimensions/metrics the caller asked\n * the analytics provider to return. If the persisted rows contain\n * `userPseudoId`, `clientId`, IP-derived geolocation, or any other\n * identifier, those fields WILL reach the AI provider. The forwarding\n * is pinned by a regression test in\n * `__tests__/analytics-report-prompt.test.ts`. Callers must either\n * exclude PII-bearing dimensions before persisting, apply a column\n * allowlist at the call site, or override the prompt template via\n * `PromptOverride` to redact rows.\n *\n * Acceptable variables (always safe): human-readable names, time periods,\n * computed aggregates (row counts, totals), and high-level dimension/metric\n * labels (which are public GA4/Plausible/Matomo schema names).\n */\n\nimport {\n definePrompt,\n type ResolvedPromptAI,\n} from '@happyvertical/smrt-prompts';\n\n/**\n * `AnalyticsProperty.analyzePerformance()` prompt. Only the human-readable\n * display name, provider label, and the requested period reach the model;\n * provider IDs, API secrets, and the providerMetadata blob are excluded.\n */\nexport const smrtAnalyticsAnalyzePerformancePrompt = definePrompt({\n key: 'smrtAnalytics.property.analyzePerformance',\n template: `Analyze the analytics performance for this property over the last {period}.\nConsider: traffic trends, user engagement, conversion patterns.\nProperty: {propertyDisplayName} ({propertyProvider})`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\n/**\n * `AnalyticsReport.analyzeResults()` prompt. Surfaces report name, the\n * dimension/metric labels (which are public GA4/Plausible/Matomo schema\n * names — not user data), the date-range window, and the row count plus\n * a JSON-stringified `resultData` blob.\n *\n * **`resultData` is forwarded verbatim.** This package does not strip PII\n * from result rows — it cannot, because the row schema is determined by\n * the dimensions/metrics the caller asked the analytics provider to\n * return. If the caller persists rows containing a `userPseudoId`,\n * `clientId`, IP-derived geolocation, or any other identifier, those\n * fields WILL reach the AI provider. Either:\n *\n * - exclude PII-bearing dimensions before persisting `resultData`\n * (e.g. don't request `userPseudoId` as a GA4 dimension), or\n * - apply a column allowlist at the call site before invoking\n * `analyzeResults()`, or\n * - override the prompt template via `PromptOverride` to redact rows.\n *\n * The forwarding contract is pinned by a regression test in\n * `__tests__/analytics-report-prompt.test.ts`.\n */\nexport const smrtAnalyticsAnalyzeResultsPrompt = definePrompt({\n key: 'smrtAnalytics.report.analyzeResults',\n template: `Analyze these analytics report results and provide insights:\n\nReport: {reportName}\nDimensions: {reportDimensions}\nMetrics: {reportMetrics}\nDate Range: {dateRangeStart} to {dateRangeEnd}\nRow Count: {rowCount}\n\nData: {reportData}\n\nProvide:\n1. Key findings\n2. Trends or patterns\n3. Actionable recommendations`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\n/**\n * `AnalyticsReport.hasPositiveTrends()` boolean classifier prompt. Same\n * variables as `analyzeResults` minus the descriptive name — only the\n * provider-schema metric labels and the aggregate `resultData` payload are\n * needed to decide whether the trend is positive.\n *\n * The template explicitly instructs the model to begin its response with\n * either \"yes\" or \"no\" so the boolean coercion in `hasPositiveTrends()`\n * (`/^\\s*(yes|true)\\b/i`) is reliable. Without the leading-word\n * instruction the model commonly answers with prose that happens to\n * describe positive trends but starts with a sentence like \"The\n * conversion rate is up...\" — the parser would then treat that as\n * `false` despite a positive analysis. Tenants overriding this template\n * MUST preserve the leading yes/no convention.\n */\nexport const smrtAnalyticsHasPositiveTrendsPrompt = definePrompt({\n key: 'smrtAnalytics.report.hasPositiveTrends',\n template: `Based on these report results, are the metrics showing positive trends?\n\nMetrics: {reportMetrics}\nData: {reportData}\n\nConsider: user growth, engagement, conversions as positive indicators.\n\nBegin your response with the single word \"yes\" or \"no\" (lower case), then\noptionally provide a one-sentence explanation. Tooling parses the leading\nword — \"yes\" or \"true\" means positive, anything else means negative.`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\n/**\n * Build the message-options object passed to `aiClient.message()` from a\n * `ResolvedPromptAI` shape. Mirrors the helper in `smrt-properties` and\n * `smrt-content` so behavior across packages is identical.\n */\nexport function promptMessageOptions(ai: ResolvedPromptAI) {\n return {\n ...(ai.params || {}),\n ...(ai.model ? { model: ai.model } : {}),\n ...(typeof ai.temperature === 'number'\n ? { temperature: ai.temperature }\n : {}),\n ...(typeof ai.maxTokens === 'number' ? { maxTokens: ai.maxTokens } : {}),\n };\n}\n","/**\n * Types and enums for smrt-analytics package\n */\n\n/**\n * Analytics provider types\n */\nexport enum AnalyticsProvider {\n GA4 = 'ga4',\n PLAUSIBLE = 'plausible',\n MATOMO = 'matomo',\n}\n\n/**\n * Status for analytics properties\n */\nexport enum AnalyticsPropertyStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n PENDING = 'pending',\n}\n\n/**\n * Data stream types (matching SDK types)\n */\nexport enum DataStreamType {\n WEB = 'WEB_DATA_STREAM',\n ANDROID = 'ANDROID_APP_DATA_STREAM',\n IOS = 'IOS_APP_DATA_STREAM',\n}\n\n/**\n * Status for data streams\n */\nexport enum DataStreamStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n}\n\n/**\n * Report status\n */\nexport enum ReportStatus {\n DRAFT = 'draft',\n SCHEDULED = 'scheduled',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n}\n\n/**\n * Report frequency for scheduled reports\n */\nexport enum ReportFrequency {\n ONCE = 'once',\n DAILY = 'daily',\n WEEKLY = 'weekly',\n MONTHLY = 'monthly',\n}\n\n/**\n * Custom dimension scope (matching SDK types)\n */\nexport enum CustomDimensionScope {\n EVENT = 'EVENT',\n USER = 'USER',\n ITEM = 'ITEM',\n}\n\n/**\n * Event tracking status\n */\nexport enum TrackingEventStatus {\n PENDING = 'pending',\n SENT = 'sent',\n FAILED = 'failed',\n}\n\n/**\n * Key event counting method (matching SDK types)\n */\nexport enum CountingMethod {\n ONCE_PER_EVENT = 'ONCE_PER_EVENT',\n ONCE_PER_SESSION = 'ONCE_PER_SESSION',\n}\n\n// ============================================================================\n// Analytics aggregation types\n// ============================================================================\n\n/**\n * Day-over-day property stats with trend calculation\n */\nexport interface PropertyStatsWithTrend {\n todayPageviews: number;\n todayUsers: number;\n yesterdayPageviews: number;\n yesterdayUsers: number;\n trend: 'up' | 'down' | 'flat';\n /**\n * Day-over-day percentage change. `null` when today grew from a zero\n * yesterday-baseline (a \"new\" surge with no finite percentage); the UI\n * renders this as \"new\" rather than 0%.\n */\n trendPercent: number | null;\n}\n\n// ============================================================================\n// SDK-compatible type definitions\n// These mirror the types from @happyvertical/analytics for when SDK is not installed\n// ============================================================================\n\n/**\n * Analytics property/site representation (SDK-compatible)\n */\nexport interface SDKProperty {\n id: string;\n name: string;\n displayName: string;\n createTime: string;\n updateTime?: string;\n timeZone?: string;\n currencyCode?: string;\n industryCategory?: string;\n serviceLevel?: 'STANDARD' | 'PREMIUM';\n}\n\n/**\n * Data stream representation (SDK-compatible)\n */\nexport interface SDKDataStream {\n id: string;\n type: 'WEB_DATA_STREAM' | 'ANDROID_APP_DATA_STREAM' | 'IOS_APP_DATA_STREAM';\n displayName: string;\n measurementId?: string;\n firebaseAppId?: string;\n defaultUri?: string;\n createTime: string;\n updateTime?: string;\n}\n\n/**\n * Custom dimension representation (SDK-compatible)\n */\nexport interface SDKCustomDimension {\n id: string;\n name: string;\n parameterName: string;\n displayName: string;\n description?: string;\n scope: 'EVENT' | 'USER' | 'ITEM';\n disallowAdsPersonalization?: boolean;\n}\n\n/**\n * Custom metric representation (SDK-compatible)\n */\nexport interface SDKCustomMetric {\n id: string;\n name: string;\n parameterName: string;\n displayName: string;\n description?: string;\n scope: 'EVENT';\n measurementUnit: string;\n restrictedMetricType?: 'COST_DATA' | 'REVENUE_DATA';\n}\n\n/**\n * Key event (conversion) representation (SDK-compatible)\n */\nexport interface SDKKeyEvent {\n id: string;\n name: string;\n eventName: string;\n createTime: string;\n countingMethod?: 'ONCE_PER_EVENT' | 'ONCE_PER_SESSION';\n defaultValue?: {\n numericValue?: number;\n currencyCode?: string;\n };\n}\n\n/**\n * Report options (SDK-compatible)\n */\nexport interface SDKReportOptions {\n dateRanges: Array<{\n startDate: string;\n endDate: string;\n name?: string;\n }>;\n dimensions?: Array<{ name: string }>;\n metrics: Array<{ name: string }>;\n offset?: number;\n limit?: number;\n orderBys?: Array<{\n metric?: { metricName: string };\n dimension?: { dimensionName: string };\n desc?: boolean;\n }>;\n keepEmptyRows?: boolean;\n returnPropertyQuota?: boolean;\n}\n\n/**\n * Report result (SDK-compatible)\n */\nexport interface SDKReportResult {\n dimensionHeaders: Array<{ name: string }>;\n metricHeaders: Array<{ name: string; type: string }>;\n rows: Array<{\n dimensionValues: Array<{ value: string }>;\n metricValues: Array<{ value: string }>;\n }>;\n rowCount?: number;\n metadata?: {\n currencyCode?: string;\n timeZone?: string;\n dataLossFromOtherRow?: boolean;\n emptyReason?: string;\n };\n}\n\n/**\n * Track event payload (SDK-compatible)\n */\nexport interface SDKTrackEvent {\n name: string;\n params?: Record<string, string | number | boolean>;\n clientId?: string;\n userId?: string;\n timestamp?: number;\n nonPersonalizedAds?: boolean;\n}\n\n/**\n * Pageview event payload (SDK-compatible)\n */\nexport interface SDKPageviewEvent {\n pagePath: string;\n pageTitle?: string;\n pageLocation?: string;\n clientId?: string;\n userId?: string;\n params?: Record<string, string | number | boolean>;\n}\n\n/**\n * Analytics provider capabilities (SDK-compatible)\n */\nexport interface AnalyticsCapabilities {\n propertyManagement: boolean;\n dataStreams: boolean;\n customDimensions: boolean;\n customMetrics: boolean;\n keyEvents: boolean;\n reporting: boolean;\n realtimeReporting: boolean;\n serverSideTracking: boolean;\n clientSideSnippet: boolean;\n userIdentification: boolean;\n batchTracking: boolean;\n}\n\n/**\n * Analytics interface (SDK-compatible)\n * This is a subset of the full SDK interface for type compatibility\n */\nexport interface AnalyticsInterface {\n createProperty(options: {\n displayName: string;\n timeZone?: string;\n currencyCode?: string;\n }): Promise<SDKProperty>;\n listProperties(): Promise<SDKProperty[]>;\n getProperty(propertyId: string): Promise<SDKProperty>;\n getDataStreams(propertyId: string): Promise<SDKDataStream[]>;\n runReport(\n propertyId: string,\n options: SDKReportOptions,\n ): Promise<SDKReportResult>;\n track(event: SDKTrackEvent): Promise<void>;\n trackPageview(pageview: SDKPageviewEvent): Promise<void>;\n trackBatch(events: SDKTrackEvent[]): Promise<void>;\n getCapabilities(): Promise<AnalyticsCapabilities>;\n}\n","/**\n * AnalyticsDataStream model - Represents a data stream for a property\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { DataStreamStatus, DataStreamType } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsDataStream}.\n */\nexport interface AnalyticsDataStreamOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n propertyId?: string;\n displayName?: string;\n streamType?: DataStreamType;\n externalId?: string;\n measurementId?: string;\n firebaseAppId?: string;\n defaultUri?: string;\n bundleId?: string;\n packageName?: string;\n status?: DataStreamStatus;\n enhancedMeasurement?: boolean;\n}\n\n/**\n * AnalyticsDataStream represents a data stream (web, iOS, Android) for an analytics property.\n *\n * @example\n * ```typescript\n * const stream = await streams.create({\n * propertyId: property.id,\n * displayName: 'Web Stream',\n * streamType: DataStreamType.WEB,\n * measurementId: 'G-XXXXXXXXXX',\n * defaultUri: 'https://example.com'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsDataStream extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Data streams expose tenant-private platform configuration (measurement\n * IDs, Firebase app IDs, bundle/package names, default URIs). Without tenant\n * scoping the generated `list`/`get` API returns every tenant's streams.\n * `@TenantScoped` auto-filters reads and binds writes to the active tenant.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent property ID (references AnalyticsProperty)\n */\n @foreignKey('AnalyticsProperty')\n propertyId: string = '';\n\n /**\n * Human-readable display name\n */\n displayName: string = '';\n\n /**\n * Stream type (WEB, ANDROID, IOS)\n */\n streamType: DataStreamType = DataStreamType.WEB;\n\n /**\n * External stream ID from provider\n */\n externalId: string = '';\n\n /**\n * Measurement ID for web streams (G-XXXXXXX)\n */\n measurementId: string = '';\n\n /**\n * Firebase App ID for app streams\n */\n firebaseAppId: string = '';\n\n /**\n * Default URI for web streams\n */\n defaultUri: string = '';\n\n /**\n * Bundle ID for iOS apps\n */\n bundleId: string = '';\n\n /**\n * Package name for Android apps\n */\n packageName: string = '';\n\n /**\n * Stream status\n */\n status: DataStreamStatus = DataStreamStatus.ACTIVE;\n\n /**\n * Enhanced measurement enabled\n */\n enhancedMeasurement: boolean = true;\n\n constructor(options: AnalyticsDataStreamOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.propertyId !== undefined) this.propertyId = options.propertyId;\n if (options.displayName !== undefined)\n this.displayName = options.displayName;\n if (options.streamType !== undefined) this.streamType = options.streamType;\n if (options.externalId !== undefined) this.externalId = options.externalId;\n if (options.measurementId !== undefined)\n this.measurementId = options.measurementId;\n if (options.firebaseAppId !== undefined)\n this.firebaseAppId = options.firebaseAppId;\n if (options.defaultUri !== undefined) this.defaultUri = options.defaultUri;\n if (options.bundleId !== undefined) this.bundleId = options.bundleId;\n if (options.packageName !== undefined)\n this.packageName = options.packageName;\n if (options.status !== undefined) this.status = options.status;\n if (options.enhancedMeasurement !== undefined)\n this.enhancedMeasurement = options.enhancedMeasurement;\n }\n\n /**\n * Check if this is a web stream\n */\n isWeb(): boolean {\n return this.streamType === DataStreamType.WEB;\n }\n\n /**\n * Check if this is an iOS app stream\n */\n isIOS(): boolean {\n return this.streamType === DataStreamType.IOS;\n }\n\n /**\n * Check if this is an Android app stream\n */\n isAndroid(): boolean {\n return this.streamType === DataStreamType.ANDROID;\n }\n\n /**\n * Check if this is a mobile app stream (iOS or Android)\n */\n isMobileApp(): boolean {\n return this.isIOS() || this.isAndroid();\n }\n\n /**\n * Get the platform identifier (measurementId for web, firebaseAppId for apps)\n */\n getPlatformId(): string {\n if (this.isWeb()) {\n return this.measurementId;\n }\n return this.firebaseAppId;\n }\n}\n\nexport default AnalyticsDataStream;\n","/**\n * AnalyticsDataStreamCollection - Collection manager for AnalyticsDataStream objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsDataStream } from '../models/AnalyticsDataStream.js';\nimport { DataStreamStatus, DataStreamType } from '../types/index.js';\n\nexport class AnalyticsDataStreamCollection extends SmrtCollection<AnalyticsDataStream> {\n static readonly _itemClass = AnalyticsDataStream;\n\n /**\n * Find streams by property\n *\n * @param propertyId - Parent property ID\n * @returns Array of data streams\n */\n async findByProperty(propertyId: string): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: { propertyId },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find stream by external ID\n *\n * @param externalId - External stream ID from provider\n * @returns Matching stream or null\n */\n async findByExternalId(\n externalId: string,\n ): Promise<AnalyticsDataStream | null> {\n const results = await this.list({\n where: { externalId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find stream by measurement ID (web streams)\n *\n * @param measurementId - GA4 measurement ID (G-XXXXXXX)\n * @returns Matching stream or null\n */\n async findByMeasurementId(\n measurementId: string,\n ): Promise<AnalyticsDataStream | null> {\n const results = await this.list({\n where: { measurementId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find streams by type\n *\n * @param streamType - Stream type (WEB, ANDROID, IOS)\n * @returns Array of matching streams\n */\n async findByType(streamType: DataStreamType): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: { streamType },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all web streams\n */\n async findWebStreams(): Promise<AnalyticsDataStream[]> {\n return await this.findByType(DataStreamType.WEB);\n }\n\n /**\n * Find all iOS app streams\n */\n async findIOSStreams(): Promise<AnalyticsDataStream[]> {\n return await this.findByType(DataStreamType.IOS);\n }\n\n /**\n * Find all Android app streams\n */\n async findAndroidStreams(): Promise<AnalyticsDataStream[]> {\n return await this.findByType(DataStreamType.ANDROID);\n }\n\n /**\n * Find all mobile app streams (iOS + Android)\n */\n async findMobileStreams(): Promise<AnalyticsDataStream[]> {\n const ios = await this.findIOSStreams();\n const android = await this.findAndroidStreams();\n return [...ios, ...android].sort((a, b) =>\n a.displayName.localeCompare(b.displayName),\n );\n }\n\n /**\n * Find streams by status\n *\n * @param status - Stream status\n * @returns Array of matching streams\n */\n async findByStatus(status: DataStreamStatus): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: { status },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all active streams\n */\n async findActive(): Promise<AnalyticsDataStream[]> {\n return await this.findByStatus(DataStreamStatus.ACTIVE);\n }\n\n /**\n * Find active streams for a property\n *\n * @param propertyId - Parent property ID\n * @returns Array of active streams\n */\n async findActiveByProperty(\n propertyId: string,\n ): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: {\n propertyId,\n status: DataStreamStatus.ACTIVE,\n },\n orderBy: 'displayName ASC',\n });\n }\n}\n","/**\n * AnalyticsEvent model - Tracked events log\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { TrackingEventStatus } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsEvent}.\n */\nexport interface AnalyticsEventOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n propertyId?: string;\n eventName?: string;\n clientId?: string;\n userId?: string;\n params?: string;\n eventTimestamp?: Date;\n status?: TrackingEventStatus;\n sentAt?: Date | null;\n errorMessage?: string;\n retryCount?: number;\n nonPersonalizedAds?: boolean;\n sessionId?: string;\n pagePath?: string;\n pageTitle?: string;\n userAgent?: string;\n ipAddress?: string;\n}\n\n/**\n * AnalyticsEvent represents a tracked analytics event (server-side tracking log).\n *\n * @example\n * ```typescript\n * const event = await events.create({\n * propertyId: property.id,\n * eventName: 'purchase',\n * clientId: 'user-123',\n * params: JSON.stringify({ value: 99.99, currency: 'USD' })\n * });\n * await event.send();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create'] },\n mcp: { include: ['list', 'get', 'track'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsEvent extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Analytics events carry end-user PII (`userId`, `clientId`, `ipAddress`,\n * `userAgent`, `sessionId`). Without tenant scoping the generated\n * `list`/`get` API leaks every tenant's visitor data, and `create` lets a\n * caller write events bound to another tenant's `propertyId`. `@TenantScoped`\n * auto-filters reads and binds writes to the active tenant context.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent property ID (references AnalyticsProperty)\n */\n @foreignKey('AnalyticsProperty')\n propertyId: string = '';\n\n /**\n * Event name (e.g., 'purchase', 'page_view', 'sign_up')\n */\n eventName: string = '';\n\n /**\n * Client ID for anonymous tracking\n */\n clientId: string = '';\n\n /**\n * User ID for identified tracking\n */\n userId: string = '';\n\n /**\n * Event parameters (JSON)\n */\n params: string = '{}';\n\n /**\n * Event timestamp\n */\n eventTimestamp: Date = new Date();\n\n /**\n * Tracking status\n */\n status: TrackingEventStatus = TrackingEventStatus.PENDING;\n\n /**\n * Timestamp when sent to provider\n */\n sentAt: Date | null = null;\n\n /**\n * Error message if sending failed\n */\n errorMessage: string = '';\n\n /**\n * Retry count\n */\n retryCount: number = 0;\n\n /**\n * Disable personalized ads\n */\n nonPersonalizedAds: boolean = false;\n\n /**\n * Session ID\n */\n sessionId: string = '';\n\n /**\n * Page path (for pageview events)\n */\n pagePath: string = '';\n\n /**\n * Page title (for pageview events)\n */\n pageTitle: string = '';\n\n /**\n * User agent\n */\n userAgent: string = '';\n\n /**\n * IP address (anonymized)\n */\n ipAddress: string = '';\n\n constructor(options: AnalyticsEventOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.propertyId !== undefined) this.propertyId = options.propertyId;\n if (options.eventName !== undefined) this.eventName = options.eventName;\n if (options.clientId !== undefined) this.clientId = options.clientId;\n if (options.userId !== undefined) this.userId = options.userId;\n if (options.params !== undefined) this.params = options.params;\n if (options.eventTimestamp !== undefined)\n this.eventTimestamp = options.eventTimestamp;\n if (options.status !== undefined) this.status = options.status;\n if (options.sentAt !== undefined) this.sentAt = options.sentAt;\n if (options.errorMessage !== undefined)\n this.errorMessage = options.errorMessage;\n if (options.retryCount !== undefined) this.retryCount = options.retryCount;\n if (options.nonPersonalizedAds !== undefined)\n this.nonPersonalizedAds = options.nonPersonalizedAds;\n if (options.sessionId !== undefined) this.sessionId = options.sessionId;\n if (options.pagePath !== undefined) this.pagePath = options.pagePath;\n if (options.pageTitle !== undefined) this.pageTitle = options.pageTitle;\n if (options.userAgent !== undefined) this.userAgent = options.userAgent;\n if (options.ipAddress !== undefined) this.ipAddress = options.ipAddress;\n }\n\n /**\n * Get parsed event parameters\n */\n getParams(): Record<string, string | number | boolean> {\n try {\n return JSON.parse(this.params);\n } catch {\n return {};\n }\n }\n\n /**\n * Set event parameters\n */\n setParams(params: Record<string, string | number | boolean>): void {\n this.params = JSON.stringify(params);\n }\n\n /**\n * Add a parameter to the event\n */\n addParam(key: string, value: string | number | boolean): void {\n const params = this.getParams();\n params[key] = value;\n this.setParams(params);\n }\n\n /**\n * Check if this is a pageview event\n */\n isPageview(): boolean {\n return this.eventName === 'page_view' || this.eventName === 'pageview';\n }\n\n /**\n * Check if this is a conversion event\n */\n isConversion(): boolean {\n const conversionEvents = [\n 'purchase',\n 'sign_up',\n 'generate_lead',\n 'begin_checkout',\n ];\n return conversionEvents.includes(this.eventName);\n }\n\n /**\n * Mark as sent successfully\n */\n markSent(): void {\n this.status = TrackingEventStatus.SENT;\n this.sentAt = new Date();\n this.errorMessage = '';\n }\n\n /**\n * Mark as failed\n */\n markFailed(error: string): void {\n this.status = TrackingEventStatus.FAILED;\n this.errorMessage = error;\n this.retryCount++;\n }\n\n /**\n * Reset for retry\n */\n resetForRetry(): void {\n this.status = TrackingEventStatus.PENDING;\n this.sentAt = null;\n }\n\n /**\n * Check if event should be retried\n */\n shouldRetry(maxRetries: number = 3): boolean {\n return (\n this.status === TrackingEventStatus.FAILED && this.retryCount < maxRetries\n );\n }\n\n /**\n * Build SDK TrackEvent object\n */\n toTrackEvent(): {\n name: string;\n params?: Record<string, string | number | boolean>;\n clientId?: string;\n userId?: string;\n timestamp?: number;\n nonPersonalizedAds?: boolean;\n } {\n return {\n name: this.eventName,\n params: this.getParams(),\n clientId: this.clientId || undefined,\n userId: this.userId || undefined,\n timestamp: this.eventTimestamp.getTime() * 1000, // Convert to microseconds\n nonPersonalizedAds: this.nonPersonalizedAds,\n };\n }\n}\n\nexport default AnalyticsEvent;\n","/**\n * AnalyticsEventCollection - Collection manager for AnalyticsEvent objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsEvent } from '../models/AnalyticsEvent.js';\nimport {\n type PropertyStatsWithTrend,\n TrackingEventStatus,\n} from '../types/index.js';\n\nexport class AnalyticsEventCollection extends SmrtCollection<AnalyticsEvent> {\n static readonly _itemClass = AnalyticsEvent;\n\n /**\n * Offset of `timeZone` at `instant`, in milliseconds (wall-clock minus UTC).\n *\n * Reads the zone's wall-clock Y/M/D h:m:s for `instant` via\n * `Intl.DateTimeFormat` parts and subtracts the real UTC instant. Positive\n * east of UTC, negative west (e.g. `America/Los_Angeles` returns roughly\n * `-7h`/`-8h` depending on DST).\n *\n * @throws RangeError if `timeZone` is not a valid IANA identifier.\n */\n private zoneOffsetMs(instant: Date, timeZone: string): number {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n }).formatToParts(instant);\n const lookup = (type: Intl.DateTimeFormatPartTypes): number =>\n Number.parseInt(parts.find((p) => p.type === type)?.value ?? '0', 10);\n let hour = lookup('hour');\n // Intl can emit hour \"24\" for midnight under hour12:false; normalize to 0.\n if (hour === 24) hour = 0;\n const asUtc = Date.UTC(\n lookup('year'),\n lookup('month') - 1,\n lookup('day'),\n hour,\n lookup('minute'),\n lookup('second'),\n );\n return asUtc - instant.getTime();\n }\n\n /**\n * Resolve the UTC instant marking the start of the calendar day (00:00) that\n * `instant` falls on **within the given IANA time zone**.\n *\n * Day-over-day buckets (\"today vs yesterday\") must respect the property's\n * configured `timeZone` (defaults to `America/Los_Angeles`), otherwise a\n * pageview at 11:30pm local time — already the next UTC day — is bucketed\n * into the wrong day. We read the wall-clock civil date for the zone, then\n * map that date's local midnight back to a UTC instant, correcting for the\n * zone offset (and re-correcting once across a DST boundary).\n *\n * Invalid/unknown zone identifiers fall back to UTC day boundaries (matching\n * the previous behaviour) rather than throwing.\n *\n * @param instant - Reference instant.\n * @param timeZone - IANA time zone (e.g. `America/Los_Angeles`).\n * @returns UTC `Date` for local midnight of the day `instant` is in.\n */\n protected startOfDayInZone(instant: Date, timeZone: string): Date {\n let civil: { year: number; month: number; day: number };\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(instant);\n const lookup = (type: Intl.DateTimeFormatPartTypes): number =>\n Number.parseInt(parts.find((p) => p.type === type)?.value ?? '0', 10);\n civil = {\n year: lookup('year'),\n month: lookup('month'),\n day: lookup('day'),\n };\n } catch {\n // Unknown/invalid time zone -> UTC day boundary.\n return new Date(\n Date.UTC(\n instant.getUTCFullYear(),\n instant.getUTCMonth(),\n instant.getUTCDate(),\n ),\n );\n }\n\n // Local midnight of `civil`, as a UTC instant: treat civil-midnight as if\n // UTC, then subtract the zone offset at that guess. Re-derive the offset at\n // the corrected instant and re-correct once if it changed (DST edge).\n const guess = Date.UTC(civil.year, civil.month - 1, civil.day);\n const offset = this.zoneOffsetMs(new Date(guess), timeZone);\n let utc = guess - offset;\n const offset2 = this.zoneOffsetMs(new Date(utc), timeZone);\n if (offset2 !== offset) utc = guess - offset2;\n return new Date(utc);\n }\n\n /**\n * Resolve the UTC instant for the start of the day *before* `todayStart`'s\n * local day, in `timeZone`.\n *\n * Steps back 12h from local midnight (landing safely inside the previous\n * civil day regardless of DST — a naive `- 24h` skips a day across\n * spring-forward), then re-resolves start-of-day.\n *\n * @param todayStart - Local-midnight UTC instant from {@link startOfDayInZone}.\n * @param timeZone - IANA time zone.\n * @returns UTC `Date` for local midnight of the prior calendar day.\n */\n protected startOfYesterdayInZone(todayStart: Date, timeZone: string): Date {\n return this.startOfDayInZone(\n new Date(todayStart.getTime() - 12 * 3_600_000),\n timeZone,\n );\n }\n\n /**\n * Classify a day-over-day change into a trend direction + percent.\n *\n * - `yesterday > 0`: percent = rounded delta; >5% up, <-5% down, else flat.\n * - `yesterday === 0 && today > 0`: a brand-new surge from a zero baseline —\n * classified `up` with a `null` percent (no finite percentage exists), so\n * the UI renders \"new\" rather than a misleading flat 0%.\n * - `yesterday === 0 && today === 0`: flat, 0%.\n *\n * @param today - Today's count.\n * @param yesterday - Yesterday's count.\n * @returns Trend direction and percent (null when growing from zero).\n */\n protected classifyTrend(\n today: number,\n yesterday: number,\n ): { trend: 'up' | 'down' | 'flat'; trendPercent: number | null } {\n if (yesterday > 0) {\n const change = ((today - yesterday) / yesterday) * 100;\n const trendPercent = Math.round(change);\n let trend: 'up' | 'down' | 'flat' = 'flat';\n if (change > 5) trend = 'up';\n else if (change < -5) trend = 'down';\n return { trend, trendPercent };\n }\n if (today > 0) {\n // Growth from a zero baseline: a real surge, no finite percentage.\n return { trend: 'up', trendPercent: null };\n }\n return { trend: 'flat', trendPercent: 0 };\n }\n\n /**\n * Find events by property\n *\n * @param propertyId - Parent property ID\n * @returns Array of events\n */\n async findByProperty(propertyId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { propertyId },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by event name\n *\n * @param eventName - Event name to filter by\n * @returns Array of matching events\n */\n async findByEventName(eventName: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { eventName },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by client ID\n *\n * @param clientId - Client ID\n * @returns Array of events for this client\n */\n async findByClientId(clientId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { clientId },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by user ID\n *\n * @param userId - User ID\n * @returns Array of events for this user\n */\n async findByUserId(userId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { userId },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by status\n *\n * @param status - Tracking event status\n * @returns Array of matching events\n */\n async findByStatus(status: TrackingEventStatus): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { status },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find all pending events\n */\n async findPending(): Promise<AnalyticsEvent[]> {\n return await this.findByStatus(TrackingEventStatus.PENDING);\n }\n\n /**\n * Find all sent events\n */\n async findSent(): Promise<AnalyticsEvent[]> {\n return await this.findByStatus(TrackingEventStatus.SENT);\n }\n\n /**\n * Find all failed events\n */\n async findFailed(): Promise<AnalyticsEvent[]> {\n return await this.findByStatus(TrackingEventStatus.FAILED);\n }\n\n /**\n * Find events that should be retried\n *\n * @param maxRetries - Maximum retry count\n * @returns Array of events eligible for retry\n */\n async findForRetry(maxRetries: number = 3): Promise<AnalyticsEvent[]> {\n const failed = await this.findFailed();\n return failed.filter((e) => e.shouldRetry(maxRetries));\n }\n\n /**\n * Find pending events for a property\n *\n * @param propertyId - Parent property ID\n * @returns Array of pending events\n */\n async findPendingByProperty(propertyId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: {\n propertyId,\n status: TrackingEventStatus.PENDING,\n },\n orderBy: 'eventTimestamp ASC', // Process oldest first\n });\n }\n\n /**\n * Find events by date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of events in date range\n */\n async findByDateRange(\n startDate: Date,\n endDate: Date,\n ): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: {\n 'eventTimestamp >=': startDate.toISOString(),\n 'eventTimestamp <=': endDate.toISOString(),\n },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find conversion events\n *\n * @param propertyId - Optional property ID filter\n * @returns Array of conversion events\n */\n async findConversions(propertyId?: string): Promise<AnalyticsEvent[]> {\n const conversionEvents = [\n 'purchase',\n 'sign_up',\n 'generate_lead',\n 'begin_checkout',\n ];\n const all = propertyId\n ? await this.findByProperty(propertyId)\n : await this.list({ orderBy: 'eventTimestamp DESC' });\n\n return all.filter((e) => conversionEvents.includes(e.eventName));\n }\n\n /**\n * Find pageview events\n *\n * @param propertyId - Optional property ID filter\n * @returns Array of pageview events\n */\n async findPageviews(propertyId?: string): Promise<AnalyticsEvent[]> {\n const where: Record<string, unknown> = { eventName: 'page_view' };\n if (propertyId) {\n where.propertyId = propertyId;\n }\n return await this.list({\n where,\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Count events by event name for a property\n *\n * @param propertyId - Property ID\n * @returns Map of event name to count\n */\n async countByEventName(propertyId: string): Promise<Map<string, number>> {\n const events = await this.findByProperty(propertyId);\n const counts = new Map<string, number>();\n\n for (const event of events) {\n const current = counts.get(event.eventName) || 0;\n counts.set(event.eventName, current + 1);\n }\n\n return counts;\n }\n\n /**\n * Get event stats for a property\n *\n * @param propertyId - Property ID\n * @returns Event statistics\n */\n async getPropertyStats(propertyId: string): Promise<{\n total: number;\n pending: number;\n sent: number;\n failed: number;\n conversions: number;\n pageviews: number;\n }> {\n const events = await this.findByProperty(propertyId);\n\n return {\n total: events.length,\n pending: events.filter((e) => e.status === TrackingEventStatus.PENDING)\n .length,\n sent: events.filter((e) => e.status === TrackingEventStatus.SENT).length,\n failed: events.filter((e) => e.status === TrackingEventStatus.FAILED)\n .length,\n conversions: events.filter((e) => e.isConversion()).length,\n pageviews: events.filter((e) => e.isPageview()).length,\n };\n }\n\n /**\n * Get day-over-day pageview stats with trend for a property.\n *\n * Compares today's pageview count against yesterday's to produce a\n * trend direction and percentage change. A threshold of 5% is used\n * to classify 'up' vs 'down' vs 'flat'; growth from a zero baseline is\n * classified `up` with a `null` percent (see {@link classifyTrend}).\n *\n * Day boundaries are computed in `timeZone` (an IANA identifier such as the\n * property's `AnalyticsProperty.timeZone`, which defaults to\n * `America/Los_Angeles`) so an event near local midnight buckets into the\n * correct calendar day. Defaults to `'UTC'` when omitted.\n *\n * @param propertyId - Property ID\n * @param now - Optional current date (for testing)\n * @param timeZone - IANA time zone for day boundaries (default `'UTC'`)\n * @returns Stats with trend\n */\n async getPropertyStatsWithTrend(\n propertyId: string,\n now?: Date,\n timeZone: string = 'UTC',\n ): Promise<PropertyStatsWithTrend> {\n const currentTime = now || new Date();\n const todayStart = this.startOfDayInZone(currentTime, timeZone);\n const yesterdayStart = this.startOfYesterdayInZone(todayStart, timeZone);\n\n // Single query for both days to avoid concurrent DuckDB prepared statements\n const allPageviewEvents = await this.list({\n where: {\n propertyId,\n eventName: 'page_view',\n 'eventTimestamp >=': yesterdayStart.toISOString(),\n 'eventTimestamp <=': currentTime.toISOString(),\n },\n });\n\n const todayPageviewEvents = allPageviewEvents.filter(\n (e) => new Date(e.eventTimestamp) >= todayStart,\n );\n const yesterdayPageviewEvents = allPageviewEvents.filter(\n (e) => new Date(e.eventTimestamp) < todayStart,\n );\n\n // Count unique clients (users)\n const todayClients = new Set(todayPageviewEvents.map((e) => e.clientId));\n const yesterdayClients = new Set(\n yesterdayPageviewEvents.map((e) => e.clientId),\n );\n\n const todayPageviews = todayPageviewEvents.length;\n const yesterdayPageviews = yesterdayPageviewEvents.length;\n\n const { trend, trendPercent } = this.classifyTrend(\n todayPageviews,\n yesterdayPageviews,\n );\n\n return {\n todayPageviews,\n todayUsers: todayClients.size,\n yesterdayPageviews,\n yesterdayUsers: yesterdayClients.size,\n trend,\n trendPercent,\n };\n }\n\n /**\n * Get day-over-day stats for multiple properties in batch.\n *\n * Day boundaries are computed in `timeZone` (default `'UTC'`); see\n * {@link getPropertyStatsWithTrend}. A single zone applies to the whole\n * batch, so callers mixing properties with different `timeZone` values\n * should batch per zone (or fall back to per-property calls).\n *\n * @param propertyIds - Array of property IDs\n * @param now - Optional current date (for testing)\n * @param timeZone - IANA time zone for day boundaries (default `'UTC'`)\n * @returns Map of propertyId to stats\n */\n async getBatchPropertyStats(\n propertyIds: string[],\n now?: Date,\n timeZone: string = 'UTC',\n ): Promise<Map<string, PropertyStatsWithTrend>> {\n const results = new Map<string, PropertyStatsWithTrend>();\n\n // Fetch all date-ranged events once to avoid N+1 queries\n const currentTime = now || new Date();\n const todayStart = this.startOfDayInZone(currentTime, timeZone);\n const yesterdayStart = this.startOfYesterdayInZone(todayStart, timeZone);\n\n // Single query for both days to avoid concurrent DuckDB prepared statements\n const allEvents = await this.list({\n where: {\n eventName: 'page_view',\n 'eventTimestamp >=': yesterdayStart.toISOString(),\n 'eventTimestamp <=': currentTime.toISOString(),\n },\n });\n\n // Pre-group events by propertyId and day in a single pass\n const todayByProperty = new Map<string, AnalyticsEvent[]>();\n const yesterdayByProperty = new Map<string, AnalyticsEvent[]>();\n for (const e of allEvents) {\n const isToday = new Date(e.eventTimestamp) >= todayStart;\n const map = isToday ? todayByProperty : yesterdayByProperty;\n const list = map.get(e.propertyId);\n if (list) list.push(e);\n else map.set(e.propertyId, [e]);\n }\n\n for (const propertyId of propertyIds) {\n const todayPageviewEvents = todayByProperty.get(propertyId) ?? [];\n const yesterdayPageviewEvents = yesterdayByProperty.get(propertyId) ?? [];\n\n const todayClients = new Set(todayPageviewEvents.map((e) => e.clientId));\n const yesterdayClients = new Set(\n yesterdayPageviewEvents.map((e) => e.clientId),\n );\n\n const todayPageviews = todayPageviewEvents.length;\n const yesterdayPageviews = yesterdayPageviewEvents.length;\n\n const { trend, trendPercent } = this.classifyTrend(\n todayPageviews,\n yesterdayPageviews,\n );\n\n results.set(propertyId, {\n todayPageviews,\n todayUsers: todayClients.size,\n yesterdayPageviews,\n yesterdayUsers: yesterdayClients.size,\n trend,\n trendPercent,\n });\n }\n\n return results;\n }\n}\n","/**\n * AnalyticsProperty model - Represents an analytics property/site\n * @packageDocumentation\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n promptMessageOptions,\n smrtAnalyticsAnalyzePerformancePrompt,\n} from '../prompts.js';\nimport { AnalyticsPropertyStatus, AnalyticsProvider } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsProperty}.\n */\nexport interface AnalyticsPropertyOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n name?: string;\n displayName?: string;\n provider?: AnalyticsProvider;\n externalId?: string;\n measurementId?: string;\n apiSecret?: string;\n siteDomain?: string;\n timeZone?: string;\n currencyCode?: string;\n industryCategory?: string;\n serviceLevel?: string;\n status?: AnalyticsPropertyStatus;\n lastSyncAt?: Date | null;\n providerMetadata?: string;\n}\n\n/**\n * AnalyticsProperty represents an analytics property (GA4 property,\n * Plausible site, or Matomo site).\n *\n * @example\n * ```typescript\n * const property = await properties.create({\n * name: 'My Website',\n * displayName: 'My Website Analytics',\n * provider: AnalyticsProvider.GA4,\n * externalId: 'properties/123456789',\n * measurementId: 'G-XXXXXXXXXX'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'sync', 'runReport'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsProperty extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Without tenant scoping the generated `list`/`get` API and any raw query\n * return every tenant's properties — including the `@field({ sensitive })`\n * `apiSecret` / `providerMetadata` credentials. `@TenantScoped` registers\n * this class with the tenant interceptor so reads are auto-filtered and\n * writes are bound to the active tenant context.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Internal name/identifier\n */\n name: string = '';\n\n /**\n * Human-readable display name\n */\n @field({ description: 'The name people see for this website or app.' })\n displayName: string = '';\n\n /**\n * Analytics provider (ga4, plausible, matomo)\n */\n @field({\n description:\n 'Which analytics service tracks it: Google Analytics 4, Plausible or Matomo.',\n })\n provider: AnalyticsProvider = AnalyticsProvider.GA4;\n\n /**\n * External ID from the provider (e.g., \"properties/123456789\" for GA4,\n * idSite for Matomo)\n */\n @field({\n description:\n 'The id the analytics service gives the property, such as properties/123456789 or a Matomo site id.',\n })\n externalId: string = '';\n\n /**\n * Measurement ID for GA4 (G-XXXXXXXXXX)\n */\n @field({\n description: 'The tracking id for a web stream, such as G-XXXXXXXXXX.',\n })\n measurementId: string = '';\n\n /**\n * Provider API secret/token (GA4 API secret, Matomo token_auth)\n *\n * Sensitive (#1540): excluded from generated API/MCP responses and rejected\n * as a `where` filter key so it can't be probed.\n */\n @field({\n sensitive: true,\n description:\n 'The secret key your analytics service issues for this property. It is kept out of responses and tool results, but it is not encrypted in the database.',\n })\n apiSecret: string = '';\n\n /**\n * Site domain for Plausible/Matomo\n */\n @field({ description: 'The website address, used by Plausible and Matomo.' })\n siteDomain: string = '';\n\n /**\n * Property timezone\n */\n @field({ description: 'The time zone the figures are reported in.' })\n timeZone: string = 'America/Los_Angeles';\n\n /**\n * Currency code (e.g., 'USD', 'EUR')\n */\n @field({\n description: 'The currency used for revenue figures, such as USD.',\n })\n currencyCode: string = 'USD';\n\n /**\n * Industry category\n */\n industryCategory: string = '';\n\n /**\n * Service level (STANDARD, PREMIUM)\n */\n serviceLevel: string = 'STANDARD';\n\n /**\n * Property status\n */\n @field({\n description: 'Whether tracking is active, inactive or still being set up.',\n })\n status: AnalyticsPropertyStatus = AnalyticsPropertyStatus.ACTIVE;\n\n /**\n * Last sync timestamp with provider\n */\n @field({\n description:\n 'When the property was last synced with the analytics service.',\n })\n lastSyncAt: Date | null = null;\n\n /**\n * Metadata from provider (JSON)\n *\n * Sensitive (#1540): may carry provider credentials/tokens, so it is excluded\n * from generated API/MCP responses and rejected as a `where` filter key.\n */\n @field({\n sensitive: true,\n description:\n 'Extra connection details from your analytics service. They are kept out of responses and tool results, but they are not encrypted in the database.',\n })\n providerMetadata: string = '{}';\n\n constructor(options: AnalyticsPropertyOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.displayName !== undefined)\n this.displayName = options.displayName;\n if (options.provider !== undefined) this.provider = options.provider;\n if (options.externalId !== undefined) this.externalId = options.externalId;\n if (options.measurementId !== undefined)\n this.measurementId = options.measurementId;\n if (options.apiSecret !== undefined) this.apiSecret = options.apiSecret;\n if (options.siteDomain !== undefined) this.siteDomain = options.siteDomain;\n if (options.timeZone !== undefined) this.timeZone = options.timeZone;\n if (options.currencyCode !== undefined)\n this.currencyCode = options.currencyCode;\n if (options.industryCategory !== undefined)\n this.industryCategory = options.industryCategory;\n if (options.serviceLevel !== undefined)\n this.serviceLevel = options.serviceLevel;\n if (options.status !== undefined) this.status = options.status;\n if (options.lastSyncAt !== undefined) this.lastSyncAt = options.lastSyncAt;\n if (options.providerMetadata !== undefined)\n this.providerMetadata = options.providerMetadata;\n }\n\n /**\n * Check if this is a GA4 property\n */\n isGA4(): boolean {\n return this.provider === AnalyticsProvider.GA4;\n }\n\n /**\n * Check if this is a Plausible site\n */\n isPlausible(): boolean {\n return this.provider === AnalyticsProvider.PLAUSIBLE;\n }\n\n /**\n * Check if this is a Matomo site\n */\n isMatomo(): boolean {\n return this.provider === AnalyticsProvider.MATOMO;\n }\n\n /**\n * Get parsed provider metadata\n */\n getProviderMetadata(): Record<string, unknown> {\n try {\n return JSON.parse(this.providerMetadata);\n } catch {\n return {};\n }\n }\n\n /**\n * Set provider metadata\n */\n setProviderMetadata(metadata: Record<string, unknown>): void {\n this.providerMetadata = JSON.stringify(metadata);\n }\n\n /**\n * Mark as synced with provider\n */\n markSynced(): void {\n this.lastSyncAt = new Date();\n }\n\n /**\n * AI-powered: Analyze property performance.\n *\n * Uses the `smrtAnalytics.property.analyzePerformance` prompt registered\n * via `@happyvertical/smrt-prompts`, allowing tenant- or instance-level\n * overrides of the template, model, and parameters at runtime.\n *\n * Only non-PII fields (display name, provider label, requested period)\n * are sent to the AI provider. Internal identifiers (`id`, `externalId`,\n * `measurementId`, `apiSecret`, `providerMetadata`) are intentionally\n * excluded — see `../prompts.ts` for the full exclusion rationale.\n */\n async analyzePerformance(options: { period?: string } = {}): Promise<{\n action: string;\n period: string;\n analysis: string;\n }> {\n const period = options.period || '30 days';\n\n // Resolve `db` from either the canonical `db` option or its `persistence`\n // alias so stored prompt overrides are honored on first call before\n // `getAiClient()` triggers full initialization. SmrtObject already types\n // both options on `SmrtClassOptions` so no `any` cast is needed — that\n // keeps a misspelt option name surfacing as a TypeScript error rather\n // than silently falling through.\n const db = this.options.db ?? this.options.persistence;\n\n // This model is `@TenantScoped` and declares a `tenantId` field, but we\n // still deliberately OMIT `tenantId` from the resolvePrompt options so the\n // resolver falls back to the AsyncLocalStorage tenancy context via\n // `getTenantId()`. Passing `this.tenantId` (which may be null for\n // tenant-agnostic rows) explicitly would short-circuit that fallback and\n // silently ignore tenant-specific prompt overrides active in the\n // surrounding `withTenant(...)` block.\n const resolvedPrompt = await resolvePrompt(\n smrtAnalyticsAnalyzePerformancePrompt.key,\n {\n db,\n variables: {\n period,\n propertyDisplayName: this.displayName || '',\n propertyProvider: this.provider || '',\n },\n },\n );\n\n const ai = await this.getAiClient();\n const analysis = await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n );\n\n return {\n action: 'analyzePerformance',\n period,\n analysis: analysis.trim(),\n };\n }\n\n /**\n * AI-powered: Check if property is performing well\n */\n async isPerformingWell(): Promise<boolean> {\n return await this.is(\n `\n Based on the property configuration and metadata:\n - Property: ${this.displayName}\n - Provider: ${this.provider}\n - Status: ${this.status}\n - Last sync: ${this.lastSyncAt}\n\n Is this property properly configured and likely performing well?\n `,\n // Property fields hand-rolled above; skip is()'s object-data injection.\n { includeData: false },\n );\n }\n}\n\nexport default AnalyticsProperty;\n","/**\n * AnalyticsPropertyCollection - Collection manager for AnalyticsProperty objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsProperty } from '../models/AnalyticsProperty.js';\nimport { AnalyticsPropertyStatus, AnalyticsProvider } from '../types/index.js';\n\nexport class AnalyticsPropertyCollection extends SmrtCollection<AnalyticsProperty> {\n static readonly _itemClass = AnalyticsProperty;\n\n /**\n * Find property by external ID\n *\n * @param externalId - External ID from provider\n * @returns Matching property or null\n */\n async findByExternalId(\n externalId: string,\n ): Promise<AnalyticsProperty | null> {\n const results = await this.list({\n where: { externalId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find property by measurement ID (GA4)\n *\n * @param measurementId - GA4 measurement ID (G-XXXXXXXXXX)\n * @returns Matching property or null\n */\n async findByMeasurementId(\n measurementId: string,\n ): Promise<AnalyticsProperty | null> {\n const results = await this.list({\n where: { measurementId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find property by site domain and optional provider.\n *\n * @param siteDomain - Provider site domain\n * @param provider - Optional provider discriminator for migration/coexistence\n * @returns Matching property or null\n */\n async findBySiteDomain(\n siteDomain: string,\n provider?: AnalyticsProvider,\n ): Promise<AnalyticsProperty | null> {\n const results = await this.list({\n where: provider ? { siteDomain, provider } : { siteDomain },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find properties by provider\n *\n * @param provider - Analytics provider\n * @returns Array of matching properties\n */\n async findByProvider(\n provider: AnalyticsProvider,\n ): Promise<AnalyticsProperty[]> {\n return await this.list({\n where: { provider },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all GA4 properties\n */\n async findGA4Properties(): Promise<AnalyticsProperty[]> {\n return await this.findByProvider(AnalyticsProvider.GA4);\n }\n\n /**\n * Find all Plausible sites\n */\n async findPlausibleSites(): Promise<AnalyticsProperty[]> {\n return await this.findByProvider(AnalyticsProvider.PLAUSIBLE);\n }\n\n /**\n * Find all Matomo sites\n */\n async findMatomoSites(): Promise<AnalyticsProperty[]> {\n return await this.findByProvider(AnalyticsProvider.MATOMO);\n }\n\n /**\n * Find properties by status\n *\n * @param status - Property status\n * @returns Array of matching properties\n */\n async findByStatus(\n status: AnalyticsPropertyStatus,\n ): Promise<AnalyticsProperty[]> {\n return await this.list({\n where: { status },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all active properties\n */\n async findActive(): Promise<AnalyticsProperty[]> {\n return await this.findByStatus(AnalyticsPropertyStatus.ACTIVE);\n }\n\n /**\n * Find properties that need syncing (not synced in last N hours)\n *\n * @param hoursAgo - Hours since last sync\n * @returns Array of properties needing sync\n */\n async findNeedingSync(hoursAgo: number = 24): Promise<AnalyticsProperty[]> {\n const cutoff = new Date();\n cutoff.setTime(cutoff.getTime() - hoursAgo * 60 * 60 * 1000);\n\n // Get properties where lastSyncAt is null or older than cutoff\n const all = await this.findActive();\n return all.filter((p) => !p.lastSyncAt || p.lastSyncAt < cutoff);\n }\n}\n","/**\n * AnalyticsReport model - Saved report configurations and results\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n promptMessageOptions,\n smrtAnalyticsAnalyzeResultsPrompt,\n smrtAnalyticsHasPositiveTrendsPrompt,\n} from '../prompts.js';\nimport { ReportFrequency, ReportStatus } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsReport}.\n *\n * `metrics` is omitted from the {@link SmrtObjectOptions} base before being\n * re-declared: the framework option carries an observability `MetricsConfig`,\n * whereas this report field is a JSON-encoded list of analytics metrics.\n */\nexport interface AnalyticsReportOptions\n extends Omit<SmrtObjectOptions, 'metrics'> {\n tenantId?: string | null;\n propertyId?: string;\n name?: string;\n description?: string;\n dimensions?: string;\n metrics?: string;\n dateRangeStart?: string;\n dateRangeEnd?: string;\n dimensionFilter?: string;\n metricFilter?: string;\n orderBy?: string;\n maxResults?: number;\n status?: ReportStatus;\n frequency?: ReportFrequency;\n lastRunAt?: Date | null;\n nextRunAt?: Date | null;\n resultData?: string;\n rowCount?: number;\n lastError?: string;\n}\n\n/**\n * AnalyticsReport represents a saved report configuration with optional scheduling.\n *\n * @example\n * ```typescript\n * const report = await reports.create({\n * propertyId: property.id,\n * name: 'Weekly Traffic Report',\n * dimensions: JSON.stringify([{ name: 'country' }, { name: 'deviceCategory' }]),\n * metrics: JSON.stringify([{ name: 'activeUsers' }, { name: 'sessions' }]),\n * frequency: ReportFrequency.WEEKLY\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsReport extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Reports persist `resultData` rows that may contain tenant-private metrics\n * and PII-bearing dimensions. Without tenant scoping the generated\n * `list`/`get` API returns every tenant's cached report data, and the\n * AI-powered `analyze`/`run` operations could run over another tenant's\n * rows. `@TenantScoped` auto-filters reads and binds writes to the tenant.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent property ID (references AnalyticsProperty)\n */\n @foreignKey('AnalyticsProperty', {\n description: 'The website or app this report is about.',\n })\n propertyId: string = '';\n\n /**\n * Report name\n */\n @field({\n description: 'A short name for the report, such as Weekly traffic.',\n })\n name: string = '';\n\n /**\n * Report description\n */\n @field({ description: 'What the report is for.' })\n description: string = '';\n\n /**\n * Dimensions to group by (JSON array)\n */\n @field({\n description:\n 'What to break the figures down by, such as country or device, as a list.',\n })\n dimensions: string = '[]';\n\n /**\n * Metrics to retrieve (JSON array)\n */\n @field({\n description: 'What to count, such as active users or sessions, as a list.',\n })\n metrics: string = '[]';\n\n /**\n * Date range start (relative or absolute)\n */\n @field({\n description:\n 'Where the period begins: a date, or a relative value such as 7daysAgo.',\n })\n dateRangeStart: string = '7daysAgo';\n\n /**\n * Date range end (relative or absolute)\n */\n @field({\n description:\n 'Where the period ends: a date, or a relative value such as today.',\n })\n dateRangeEnd: string = 'today';\n\n /**\n * Dimension filter expression (JSON)\n */\n dimensionFilter: string = '';\n\n /**\n * Metric filter expression (JSON)\n */\n metricFilter: string = '';\n\n /**\n * Sort order (JSON array)\n */\n orderBy: string = '[]';\n\n /**\n * Maximum results to return\n */\n maxResults: number = 0;\n\n /**\n * Report status\n */\n @field({\n description:\n 'Where the report stands: draft, scheduled, running, completed or failed.',\n })\n status: ReportStatus = ReportStatus.DRAFT;\n\n /**\n * Scheduling frequency\n */\n @field({\n description:\n 'How often the report repeats: once, daily, weekly or monthly.',\n })\n frequency: ReportFrequency = ReportFrequency.ONCE;\n\n /**\n * Last run timestamp\n */\n @field({ description: 'When the report last ran.' })\n lastRunAt: Date | null = null;\n\n /**\n * Next scheduled run\n */\n @field({ description: 'When the report is next due to run.' })\n nextRunAt: Date | null = null;\n\n /**\n * Cached result data (JSON)\n */\n resultData: string = '';\n\n /**\n * Row count from last run\n */\n @field({ description: 'How many rows the last run returned.' })\n rowCount: number = 0;\n\n /**\n * Error message from last failed run\n */\n @field({ description: 'Why the last run failed. Empty when it worked.' })\n lastError: string = '';\n\n constructor(options: AnalyticsReportOptions = {}) {\n // `AnalyticsReportOptions` re-types `metrics` as the report's JSON string,\n // which collides with the framework's `metrics?: MetricsConfig` observability\n // option on `SmrtObjectOptions`. The base never reads a string `metrics` as\n // config, so the value is forwarded unchanged; the assertion only reconciles\n // the two unrelated `metrics` types at the `super()` boundary.\n super(options as SmrtObjectOptions);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.propertyId !== undefined) this.propertyId = options.propertyId;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.dimensions !== undefined) this.dimensions = options.dimensions;\n if (options.metrics !== undefined) this.metrics = options.metrics;\n if (options.dateRangeStart !== undefined)\n this.dateRangeStart = options.dateRangeStart;\n if (options.dateRangeEnd !== undefined)\n this.dateRangeEnd = options.dateRangeEnd;\n if (options.dimensionFilter !== undefined)\n this.dimensionFilter = options.dimensionFilter;\n if (options.metricFilter !== undefined)\n this.metricFilter = options.metricFilter;\n if (options.orderBy !== undefined) this.orderBy = options.orderBy;\n if (options.maxResults !== undefined) this.maxResults = options.maxResults;\n if (options.status !== undefined) this.status = options.status;\n if (options.frequency !== undefined) this.frequency = options.frequency;\n if (options.lastRunAt !== undefined) this.lastRunAt = options.lastRunAt;\n if (options.nextRunAt !== undefined) this.nextRunAt = options.nextRunAt;\n if (options.resultData !== undefined) this.resultData = options.resultData;\n if (options.rowCount !== undefined) this.rowCount = options.rowCount;\n if (options.lastError !== undefined) this.lastError = options.lastError;\n }\n\n /**\n * Get parsed dimensions\n */\n getDimensions(): Array<{ name: string }> {\n try {\n return JSON.parse(this.dimensions);\n } catch {\n return [];\n }\n }\n\n /**\n * Set dimensions\n */\n setDimensions(dimensions: Array<{ name: string }>): void {\n this.dimensions = JSON.stringify(dimensions);\n }\n\n /**\n * Get parsed metrics\n */\n getMetrics(): Array<{ name: string }> {\n try {\n return JSON.parse(this.metrics);\n } catch {\n return [];\n }\n }\n\n /**\n * Set metrics\n */\n setMetrics(metrics: Array<{ name: string }>): void {\n this.metrics = JSON.stringify(metrics);\n }\n\n /**\n * Get parsed result data\n */\n getResultData(): Record<string, unknown> | null {\n if (!this.resultData) return null;\n try {\n return JSON.parse(this.resultData);\n } catch {\n return null;\n }\n }\n\n /**\n * Set result data\n */\n setResultData(data: Record<string, unknown>): void {\n this.resultData = JSON.stringify(data);\n }\n\n /**\n * Mark report as running\n */\n markRunning(): void {\n this.status = ReportStatus.RUNNING;\n this.lastError = '';\n }\n\n /**\n * Mark report as completed with results\n */\n markCompleted(rowCount: number): void {\n this.status = ReportStatus.COMPLETED;\n this.lastRunAt = new Date();\n this.rowCount = rowCount;\n this.lastError = '';\n this.calculateNextRun();\n }\n\n /**\n * Mark report as failed\n */\n markFailed(error: string): void {\n this.status = ReportStatus.FAILED;\n this.lastRunAt = new Date();\n this.lastError = error;\n this.calculateNextRun();\n }\n\n /**\n * Calculate next scheduled run based on frequency\n */\n calculateNextRun(): void {\n if (this.frequency === ReportFrequency.ONCE) {\n this.nextRunAt = null;\n return;\n }\n\n const now = new Date();\n const next = new Date(now);\n\n switch (this.frequency) {\n case ReportFrequency.DAILY:\n next.setDate(next.getDate() + 1);\n break;\n case ReportFrequency.WEEKLY:\n next.setDate(next.getDate() + 7);\n break;\n case ReportFrequency.MONTHLY:\n next.setMonth(next.getMonth() + 1);\n break;\n }\n\n this.nextRunAt = next;\n }\n\n /**\n * Check if report is due to run\n */\n isDue(): boolean {\n if (this.frequency === ReportFrequency.ONCE) {\n return this.status === ReportStatus.SCHEDULED && !this.lastRunAt;\n }\n if (!this.nextRunAt) return false;\n return new Date() >= this.nextRunAt;\n }\n\n /**\n * AI-powered: Analyze report results.\n *\n * Uses the `smrtAnalytics.report.analyzeResults` prompt registered via\n * `@happyvertical/smrt-prompts`, allowing tenant- or instance-level\n * overrides of the template, model, and parameters at runtime.\n *\n * Internal identifiers (`id`, `propertyId`, `tenantId`, `lastError`, raw\n * `dimensionFilter` / `metricFilter` JSON) are excluded from the prompt\n * variables — see `../prompts.ts` for the exclusion rationale.\n *\n * **`resultData` is FORWARDED VERBATIM.** The persisted result rows are\n * JSON-stringified into the `reportData` variable; this package cannot\n * strip PII because the row schema is determined by which dimensions /\n * metrics the caller asked the analytics provider to return. If the\n * persisted rows contain `userPseudoId`, `clientId`, IP-derived\n * geolocation, or any other identifier, those fields WILL reach the AI\n * provider. Callers are responsible for excluding PII-bearing dimensions\n * before persisting, applying a column allowlist at the call site, or\n * overriding the prompt template via `PromptOverride`. The forwarding is\n * pinned by a regression test in\n * `__tests__/analytics-report-prompt.test.ts`.\n *\n * The previous implementation issued a second freeform `this.do()` call\n * to re-summarize \"top 3 insights\"; that behaviour is now folded into\n * the single registered template (which already asks for findings,\n * trends, and recommendations) — `insights` mirrors `analysis` so the\n * return shape is preserved without a redundant AI round-trip.\n */\n async analyzeResults(_options: Record<string, unknown> = {}): Promise<{\n action: string;\n analysis: string;\n insights: string;\n }> {\n const resultData = this.getResultData();\n\n // Resolve `db` from either the canonical `db` option or its `persistence`\n // alias so stored prompt overrides are honored on first call before\n // `getAiClient()` triggers full initialization. SmrtObject already types\n // both options on `SmrtClassOptions` so no `any` cast is needed — that\n // keeps a misspelt option name surfacing as a TypeScript error rather\n // than silently falling through.\n const db = this.options.db ?? this.options.persistence;\n\n // This model is `@TenantScoped` and declares a `tenantId` field, but we\n // still deliberately OMIT `tenantId` from the resolvePrompt options so the\n // resolver falls back to the AsyncLocalStorage tenancy context via\n // `getTenantId()`. Passing `this.tenantId` (which may be null for\n // tenant-agnostic rows) explicitly would short-circuit that fallback and\n // silently ignore tenant-specific prompt overrides active in the\n // surrounding `withTenant(...)` block.\n const resolvedPrompt = await resolvePrompt(\n smrtAnalyticsAnalyzeResultsPrompt.key,\n {\n db,\n variables: {\n reportName: this.name || '',\n reportDimensions: this.dimensions || '[]',\n reportMetrics: this.metrics || '[]',\n dateRangeStart: this.dateRangeStart || '',\n dateRangeEnd: this.dateRangeEnd || '',\n rowCount: String(this.rowCount),\n reportData: JSON.stringify(resultData, null, 2),\n },\n },\n );\n\n const ai = await this.getAiClient();\n const analysis = (\n await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n )\n ).trim();\n\n return {\n action: 'analyzeResults',\n analysis,\n insights: analysis,\n };\n }\n\n /**\n * AI-powered: Check if results show positive trends.\n *\n * Uses the `smrtAnalytics.report.hasPositiveTrends` prompt registered\n * via `@happyvertical/smrt-prompts`. Only the metric labels and the\n * aggregate `resultData` JSON are sent to the AI provider — though as\n * with `analyzeResults`, `resultData` is forwarded verbatim and may\n * carry PII the caller persisted; see `analyzeResults` docstring and\n * `../prompts.ts`.\n *\n * When a typed-decision provider is configured and this object has no\n * registered tools, the resolved prompt is sent as one bounded predicate\n * request. The report's selected metrics and persisted aggregate data are\n * the complete decision state; object fields and internal filters are never\n * added. The decision uses the configured threshold (default `0.5`). A\n * configured uncertainty band falls back to the same resolved generative\n * prompt, matching `SmrtObject.evaluate()`; provider, capability, and\n * malformed-response errors propagate.\n *\n * Without a decision provider, or where tools require the generative route,\n * Boolean coercion uses `/^\\s*(yes|true)\\b/i` against the trimmed response.\n * The registered prompt template explicitly instructs the model to begin its\n * answer with the literal word \"yes\" or \"no\" so this legacy route remains\n * compatible; tenant overrides MUST preserve that leading-word convention.\n */\n async hasPositiveTrends(): Promise<boolean> {\n const resultData = this.getResultData();\n\n // See `analyzeResults` above for the typed-options + tenancy-fallback rationale.\n const db = this.options.db ?? this.options.persistence;\n\n const resolvedPrompt = await resolvePrompt(\n smrtAnalyticsHasPositiveTrendsPrompt.key,\n {\n db,\n variables: {\n reportMetrics: this.metrics || '[]',\n reportData: JSON.stringify(resultData),\n },\n },\n );\n\n // Typed decisions cannot call tools. Check this before resolving the\n // optional client so a tool-enabled report stays on its existing route.\n if (this.getAvailableTools().length === 0) {\n const config = this.getDecisionConfig();\n const threshold = config?.threshold ?? 0.5;\n if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {\n throw new Error(\n 'Decision threshold must be a finite number in [0, 1].',\n );\n }\n const uncertaintyBand = config?.uncertaintyFallback?.band;\n if (\n uncertaintyBand !== undefined &&\n (!Number.isFinite(uncertaintyBand) ||\n uncertaintyBand < 0 ||\n uncertaintyBand > 1)\n ) {\n throw new Error(\n 'Decision uncertainty fallback band must be a finite number in [0, 1].',\n );\n }\n\n const decision = await this.attemptDecision({\n state: {\n reportMetrics: this.metrics || '[]',\n reportData: JSON.stringify(resultData),\n },\n questions: {\n result: {\n type: 'predicate',\n instructions: `${resolvedPrompt.text}\\n\\nFor this predicate, only treat higher values as favorable for user growth, engagement, and conversions. Treat higher bounce or error rates as adverse. Do not infer a direction for an unknown metric. A date-range label alone does not establish comparable periods; if the supplied data is incomplete, lacks a comparable period, or is otherwise insufficient or ambiguous, answer false.`,\n },\n },\n });\n\n if (decision) {\n const answer = decision.answers.result;\n if (answer?.type !== 'predicate') {\n throw new Error('Decision provider returned no predicate result.');\n }\n\n const probability = answer.probability;\n if (uncertaintyBand !== undefined) {\n const outsideUncertaintyBand =\n probability < Math.max(0, threshold - uncertaintyBand) ||\n probability > Math.min(1, threshold + uncertaintyBand);\n if (outsideUncertaintyBand) {\n return probability >= threshold;\n }\n } else {\n return probability >= threshold;\n }\n }\n }\n\n const ai = await this.getAiClient();\n const response = (\n await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n )\n ).trim();\n\n return /^\\s*(yes|true)\\b/i.test(response);\n }\n}\n\nexport default AnalyticsReport;\n","/**\n * AnalyticsReportCollection - Collection manager for AnalyticsReport objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsReport } from '../models/AnalyticsReport.js';\nimport { ReportFrequency, ReportStatus } from '../types/index.js';\n\nexport class AnalyticsReportCollection extends SmrtCollection<AnalyticsReport> {\n static readonly _itemClass = AnalyticsReport;\n\n /**\n * Find reports by property\n *\n * @param propertyId - Parent property ID\n * @returns Array of reports\n */\n async findByProperty(propertyId: string): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { propertyId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find reports by status\n *\n * @param status - Report status\n * @returns Array of matching reports\n */\n async findByStatus(status: ReportStatus): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { status },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all draft reports\n */\n async findDrafts(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.DRAFT);\n }\n\n /**\n * Find all scheduled reports\n */\n async findScheduled(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.SCHEDULED);\n }\n\n /**\n * Find all completed reports\n */\n async findCompleted(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.COMPLETED);\n }\n\n /**\n * Find all failed reports\n */\n async findFailed(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.FAILED);\n }\n\n /**\n * Find reports by frequency\n *\n * @param frequency - Report frequency\n * @returns Array of matching reports\n */\n async findByFrequency(\n frequency: ReportFrequency,\n ): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { frequency },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all recurring reports (not one-time)\n */\n async findRecurring(): Promise<AnalyticsReport[]> {\n const all = await this.list({ orderBy: 'name ASC' });\n return all.filter((r) => r.frequency !== ReportFrequency.ONCE);\n }\n\n /**\n * Find reports due to run\n *\n * @returns Array of reports that are due\n */\n async findDue(): Promise<AnalyticsReport[]> {\n const all = await this.list({\n where: { status: ReportStatus.SCHEDULED },\n });\n return all.filter((r) => r.isDue());\n }\n\n /**\n * Find reports for a property by status\n *\n * @param propertyId - Parent property ID\n * @param status - Report status\n * @returns Array of matching reports\n */\n async findByPropertyAndStatus(\n propertyId: string,\n status: ReportStatus,\n ): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { propertyId, status },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find recently run reports\n *\n * @param hoursAgo - Hours since last run\n * @returns Array of recently run reports\n */\n async findRecentlyRun(hoursAgo: number = 24): Promise<AnalyticsReport[]> {\n const cutoff = new Date();\n cutoff.setTime(cutoff.getTime() - hoursAgo * 60 * 60 * 1000);\n\n const all = await this.list({ orderBy: 'lastRunAt DESC' });\n return all.filter((r) => r.lastRunAt && r.lastRunAt >= cutoff);\n }\n}\n","/**\n * Declared recipes for smrt-analytics (#3719): user-facing units of\n * functionality an app or agent can pick instead of the whole package.\n * The scanner reads these statics into the `recipes` array of `manifest.json`\n * and `smrt-knowledge.json`; nothing here runs at that point.\n *\n * `analytics.reports` covers properties and saved reports. Event tracking\n * (`AnalyticsEvent`) and data streams are separate concerns and are left out.\n *\n * @packageDocumentation\n */\n\nimport { SmrtRecipe } from '@happyvertical/smrt-core';\nimport { AnalyticsProperty } from './models/AnalyticsProperty.js';\nimport { AnalyticsReport } from './models/AnalyticsReport.js';\n\n/** Analytics reports: saved traffic reports and an AI read on the results. */\nexport class AnalyticsReportsRecipe extends SmrtRecipe {\n static id = 'analytics.reports';\n static help = './reports.recipe.md';\n static label = 'Analytics';\n static summary =\n 'Save traffic reports for your websites and apps and get a plain-language read on the results.';\n static synonyms = [\n 'analytics',\n 'traffic',\n 'pageviews',\n 'visitors',\n 'web stats',\n 'google analytics',\n 'plausible',\n 'matomo',\n ];\n static section = {\n id: 'analytics',\n label: 'Analytics',\n icon: 'layers',\n description: 'How your websites and apps are doing.',\n };\n static models = [AnalyticsProperty, AnalyticsReport];\n // Stored reports and their AI summaries need the app database and a server\n // held AI key; the provider credentials live on the property row. Only the\n // view component below renders in the browser, from data the server hands it.\n static runtime = 'server' as const;\n // `AnalyticsSummary` renders an empty state without data. `PropertyInfo` and\n // `EventsTable` take required row props and would fail with none, so they are\n // not routes; a host places them inside its own pages.\n static surfaces = [\n {\n kind: 'route',\n path: '/analytics/summary',\n export: '@happyvertical/smrt-analytics/svelte#AnalyticsSummary',\n label: 'Traffic summary',\n },\n ] as const;\n // Icons are shell icon names (`SHELL_ICON_PATHS` in smrt-svelte), which have\n // no chart or globe glyph; the section and entries use the closest ones.\n static nav = [\n {\n label: 'Analytics properties',\n model: AnalyticsProperty,\n icon: 'home',\n description: 'The websites and apps you track, and who tracks them.',\n },\n {\n label: 'Analytics reports',\n model: AnalyticsReport,\n icon: 'fileText',\n description: 'Saved reports, how often they run and what they found.',\n },\n ];\n // Literal values (`as const` keeps `visibility` a literal type): the scanner\n // reads these statically. Internal naming and raw query JSON stay out of the\n // way; stored result rows are read through the report, not edited by hand.\n static options = {\n AnalyticsProperty: {\n fields: {\n name: { visibility: 'hidden' },\n industryCategory: { visibility: 'advanced' },\n serviceLevel: { visibility: 'advanced' },\n lastSyncAt: { locked: true },\n },\n },\n AnalyticsReport: {\n fields: {\n dimensionFilter: { visibility: 'advanced' },\n metricFilter: { visibility: 'advanced' },\n orderBy: { visibility: 'advanced' },\n maxResults: { visibility: 'advanced' },\n resultData: { visibility: 'hidden', locked: true },\n rowCount: { locked: true },\n lastRunAt: { locked: true },\n nextRunAt: { locked: true },\n lastError: { locked: true },\n },\n },\n } as const;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["AnalyticsProvider","AnalyticsPropertyStatus","DataStreamType","DataStreamStatus","ReportStatus","ReportFrequency","CustomDimensionScope","TrackingEventStatus","CountingMethod"],"sources":["../src/__smrt-register__.ts","../src/prompts.ts","../src/types/index.ts","../src/models/AnalyticsDataStream.ts","../src/collections/AnalyticsDataStreamCollection.ts","../src/models/AnalyticsEvent.ts","../src/collections/AnalyticsEventCollection.ts","../src/models/AnalyticsProperty.ts","../src/collections/AnalyticsPropertyCollection.ts","../src/models/AnalyticsReport.ts","../src/collections/AnalyticsReportCollection.ts","../src/recipes.ts"],"sourcesContent":["// Generated by @happyvertical/smrt-core smrtPlugin at library build time.\n// The package manifest is inlined so self-registration cannot miss when a\n// downstream bundler relocates this module away from dist/manifest.json\n// (https://github.com/happyvertical/smrt/issues/1506,\n// https://github.com/happyvertical/smrt/issues/1507).\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(JSON.parse(\"{\\\"version\\\":\\\"1.0.0\\\",\\\"timestamp\\\":0,\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"packageVersion\\\":\\\"0.55.19\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-analytics:AnalyticsDataStreamCollection\\\":{\\\"name\\\":\\\"analyticsdatastreamcollection\\\",\\\"className\\\":\\\"AnalyticsDataStreamCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsDataStreamCollection\\\",\\\"collection\\\":\\\"analyticsdatastreams\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsDataStreamCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProperty\\\":{\\\"name\\\":\\\"findByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByExternalId\\\":{\\\"name\\\":\\\"findByExternalId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"externalId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByMeasurementId\\\":{\\\"name\\\":\\\"findByMeasurementId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"measurementId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByType\\\":{\\\"name\\\":\\\"findByType\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"streamType\\\",\\\"type\\\":\\\"DataStreamType\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findWebStreams\\\":{\\\"name\\\":\\\"findWebStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findIOSStreams\\\":{\\\"name\\\":\\\"findIOSStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findAndroidStreams\\\":{\\\"name\\\":\\\"findAndroidStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findMobileStreams\\\":{\\\"name\\\":\\\"findMobileStreams\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"DataStreamStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActive\\\":{\\\"name\\\":\\\"findActive\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActiveByProperty\\\":{\\\"name\\\":\\\"findActiveByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsDataStream[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsDataStream\\\",\\\"exportName\\\":\\\"AnalyticsDataStreamCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsDataStreamCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_data_streams\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_data_streams\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"stream_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"firebase_app_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"default_uri\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"bundle_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"package_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"enhanced_measurement\\\\\\\" BOOLEAN DEFAULT TRUE\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"stream_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"firebase_app_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"default_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"bundle_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"package_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"enhanced_measurement\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":true}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_data_streams_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_data_streams_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_data_streams_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_data_streams_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"851609ba\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsEventCollection\\\":{\\\"name\\\":\\\"analyticseventcollection\\\",\\\"className\\\":\\\"AnalyticsEventCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsEventCollection\\\",\\\"collection\\\":\\\"analyticsevents\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsEventCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProperty\\\":{\\\"name\\\":\\\"findByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByEventName\\\":{\\\"name\\\":\\\"findByEventName\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"eventName\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByClientId\\\":{\\\"name\\\":\\\"findByClientId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"clientId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByUserId\\\":{\\\"name\\\":\\\"findByUserId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"userId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"TrackingEventStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPending\\\":{\\\"name\\\":\\\"findPending\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findSent\\\":{\\\"name\\\":\\\"findSent\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findFailed\\\":{\\\"name\\\":\\\"findFailed\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findForRetry\\\":{\\\"name\\\":\\\"findForRetry\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"maxRetries\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPendingByProperty\\\":{\\\"name\\\":\\\"findPendingByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByDateRange\\\":{\\\"name\\\":\\\"findByDateRange\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"startDate\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"endDate\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findConversions\\\":{\\\"name\\\":\\\"findConversions\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPageviews\\\":{\\\"name\\\":\\\"findPageviews\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsEvent[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"countByEventName\\\":{\\\"name\\\":\\\"countByEventName\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Map<string, number>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPropertyStats\\\":{\\\"name\\\":\\\"getPropertyStats\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPropertyStatsWithTrend\\\":{\\\"name\\\":\\\"getPropertyStatsWithTrend\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"now\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"timeZone\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true,\\\"default\\\":\\\"UTC\\\"}],\\\"returnType\\\":\\\"Promise<PropertyStatsWithTrend>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBatchPropertyStats\\\":{\\\"name\\\":\\\"getBatchPropertyStats\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"now\\\",\\\"type\\\":\\\"Date\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"timeZone\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true,\\\"default\\\":\\\"UTC\\\"}],\\\"returnType\\\":\\\"Promise<Map<string, PropertyStatsWithTrend>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsEvent\\\",\\\"exportName\\\":\\\"AnalyticsEventCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsEventCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_events\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_events\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"event_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"client_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"params\\\\\\\" TEXT DEFAULT '{}',\\\\n \\\\\\\"event_timestamp\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"sent_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"error_message\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"retry_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"non_personalized_ads\\\\\\\" BOOLEAN DEFAULT FALSE,\\\\n \\\\\\\"session_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_path\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_title\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_agent\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"ip_address\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"event_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"client_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"params\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"},\\\"event_timestamp\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"sent_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"error_message\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"retry_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"non_personalized_ads\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":false},\\\"session_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_path\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_title\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_agent\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"ip_address\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_events_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_events_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_events_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_events_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"bee09e1d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsPropertyCollection\\\":{\\\"name\\\":\\\"analyticspropertycollection\\\",\\\"className\\\":\\\"AnalyticsPropertyCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsPropertyCollection\\\",\\\"collection\\\":\\\"analyticsproperties\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsPropertyCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByExternalId\\\":{\\\"name\\\":\\\"findByExternalId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"externalId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByMeasurementId\\\":{\\\"name\\\":\\\"findByMeasurementId\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"measurementId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findBySiteDomain\\\":{\\\"name\\\":\\\"findBySiteDomain\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"siteDomain\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"AnalyticsProvider\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByProvider\\\":{\\\"name\\\":\\\"findByProvider\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"AnalyticsProvider\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findGA4Properties\\\":{\\\"name\\\":\\\"findGA4Properties\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findPlausibleSites\\\":{\\\"name\\\":\\\"findPlausibleSites\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findMatomoSites\\\":{\\\"name\\\":\\\"findMatomoSites\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"AnalyticsPropertyStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActive\\\":{\\\"name\\\":\\\"findActive\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findNeedingSync\\\":{\\\"name\\\":\\\"findNeedingSync\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"hoursAgo\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsProperty[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsProperty\\\",\\\"exportName\\\":\\\"AnalyticsPropertyCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsPropertyCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_properties\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_properties\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"provider\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"api_secret\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"site_domain\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"time_zone\\\\\\\" TEXT DEFAULT 'America/Los_Angeles',\\\\n \\\\\\\"currency_code\\\\\\\" TEXT DEFAULT 'USD',\\\\n \\\\\\\"industry_category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"service_level\\\\\\\" TEXT DEFAULT 'STANDARD',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"last_sync_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"provider_metadata\\\\\\\" TEXT DEFAULT '{}'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"provider\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"api_secret\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"site_domain\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"time_zone\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"America/Los_Angeles\\\"},\\\"currency_code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"USD\\\"},\\\"industry_category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"service_level\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"STANDARD\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_sync_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"provider_metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_properties_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_properties_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_properties_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"f38a20b9\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsReportCollection\\\":{\\\"name\\\":\\\"analyticsreportcollection\\\",\\\"className\\\":\\\"AnalyticsReportCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsReportCollection\\\",\\\"collection\\\":\\\"analyticsreports\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/collections/AnalyticsReportCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProperty\\\":{\\\"name\\\":\\\"findByProperty\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"ReportStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findDrafts\\\":{\\\"name\\\":\\\"findDrafts\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findScheduled\\\":{\\\"name\\\":\\\"findScheduled\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findCompleted\\\":{\\\"name\\\":\\\"findCompleted\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findFailed\\\":{\\\"name\\\":\\\"findFailed\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByFrequency\\\":{\\\"name\\\":\\\"findByFrequency\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"frequency\\\",\\\"type\\\":\\\"ReportFrequency\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findRecurring\\\":{\\\"name\\\":\\\"findRecurring\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findDue\\\":{\\\"name\\\":\\\"findDue\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByPropertyAndStatus\\\":{\\\"name\\\":\\\"findByPropertyAndStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"propertyId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"ReportStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findRecentlyRun\\\":{\\\"name\\\":\\\"findRecentlyRun\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"hoursAgo\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AnalyticsReport[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AnalyticsReport\\\",\\\"exportName\\\":\\\"AnalyticsReportCollection\\\",\\\"collectionExportName\\\":\\\"AnalyticsReportCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_reports\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_reports\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"description\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"dimensions\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"metrics\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"date_range_start\\\\\\\" TEXT DEFAULT '7daysAgo',\\\\n \\\\\\\"date_range_end\\\\\\\" TEXT DEFAULT 'today',\\\\n \\\\\\\"dimension_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"metric_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"order_by\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"max_results\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"frequency\\\\\\\" TEXT,\\\\n \\\\\\\"last_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"next_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"result_data\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"row_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"last_error\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"dimensions\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"metrics\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"date_range_start\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"7daysAgo\\\"},\\\"date_range_end\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"today\\\"},\\\"dimension_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"metric_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"order_by\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"max_results\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"frequency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"next_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"result_data\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"row_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"last_error\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_reports_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_reports_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_reports_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_reports_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"22ce0f1c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsDataStream\\\":{\\\"name\\\":\\\"analyticsdatastream\\\",\\\"className\\\":\\\"AnalyticsDataStream\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsDataStream\\\",\\\"collection\\\":\\\"analyticsdatastreams\\\",\\\"description\\\":\\\"AnalyticsDataStream represents a data stream (web, iOS, Android) for an analytics property.\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsDataStream.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"propertyId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"AnalyticsProperty\\\"},\\\"displayName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"streamType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"enum\\\":[\\\"WEB_DATA_STREAM\\\",\\\"ANDROID_APP_DATA_STREAM\\\",\\\"IOS_APP_DATA_STREAM\\\"]},\\\"externalId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurementId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"firebaseAppId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"defaultUri\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"bundleId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"packageName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"enum\\\":[\\\"active\\\",\\\"inactive\\\"]},\\\"enhancedMeasurement\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":false,\\\"default\\\":true}},\\\"methods\\\":{\\\"isWeb\\\":{\\\"name\\\":\\\"isWeb\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isIOS\\\":{\\\"name\\\":\\\"isIOS\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isAndroid\\\":{\\\"name\\\":\\\"isAndroid\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isMobileApp\\\":{\\\"name\\\":\\\"isMobileApp\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPlatformId\\\":{\\\"name\\\":\\\"getPlatformId\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"string\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsDataStream\\\",\\\"collectionExportName\\\":\\\"AnalyticsDataStreamCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_data_streams\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_data_streams\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"stream_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"firebase_app_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"default_uri\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"bundle_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"package_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"enhanced_measurement\\\\\\\" BOOLEAN DEFAULT TRUE\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"stream_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"firebase_app_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"default_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"bundle_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"package_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"enhanced_measurement\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":true}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_data_streams_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_data_streams_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_data_streams_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_data_streams_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"851609ba\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsEvent\\\":{\\\"name\\\":\\\"analyticsevent\\\",\\\"className\\\":\\\"AnalyticsEvent\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsEvent\\\",\\\"collection\\\":\\\"analyticsevents\\\",\\\"description\\\":\\\"AnalyticsEvent represents a tracked analytics event (server-side tracking log).\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsEvent.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"propertyId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"AnalyticsProperty\\\"},\\\"eventName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"clientId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"userId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"params\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"{}\\\"},\\\"eventTimestamp\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"enum\\\":[\\\"pending\\\",\\\"sent\\\",\\\"failed\\\"]},\\\"sentAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"errorMessage\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"retryCount\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"nonPersonalizedAds\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":false,\\\"default\\\":false},\\\"sessionId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"pagePath\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"pageTitle\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"userAgent\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"ipAddress\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{\\\"getParams\\\":{\\\"name\\\":\\\"getParams\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, string | number | boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setParams\\\":{\\\"name\\\":\\\"setParams\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"params\\\",\\\"type\\\":\\\"Record<string, string | number | boolean>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"addParam\\\":{\\\"name\\\":\\\"addParam\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"key\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"value\\\",\\\"type\\\":\\\"string | number | boolean\\\",\\\"optional\\\":false,\\\"unionBranches\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"number\\\"},{\\\"type\\\":\\\"boolean\\\"}]}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isPageview\\\":{\\\"name\\\":\\\"isPageview\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isConversion\\\":{\\\"name\\\":\\\"isConversion\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markSent\\\":{\\\"name\\\":\\\"markSent\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markFailed\\\":{\\\"name\\\":\\\"markFailed\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"error\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"resetForRetry\\\":{\\\"name\\\":\\\"resetForRetry\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"shouldRetry\\\":{\\\"name\\\":\\\"shouldRetry\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"maxRetries\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"toTrackEvent\\\":{\\\"name\\\":\\\"toTrackEvent\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"object\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"track\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsEvent\\\",\\\"collectionExportName\\\":\\\"AnalyticsEventCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_events\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_events\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"event_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"client_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"params\\\\\\\" TEXT DEFAULT '{}',\\\\n \\\\\\\"event_timestamp\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"sent_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"error_message\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"retry_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"non_personalized_ads\\\\\\\" BOOLEAN DEFAULT FALSE,\\\\n \\\\\\\"session_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_path\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"page_title\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"user_agent\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"ip_address\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"event_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"client_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"params\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"},\\\"event_timestamp\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"sent_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"error_message\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"retry_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"non_personalized_ads\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":false},\\\"session_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_path\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"page_title\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"user_agent\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"ip_address\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_events_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_events_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_events_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_events_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"bee09e1d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\":{\\\"name\\\":\\\"analyticsproperty\\\",\\\"className\\\":\\\"AnalyticsProperty\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\",\\\"collection\\\":\\\"analyticsproperties\\\",\\\"description\\\":\\\"AnalyticsProperty represents an analytics property (GA4 property, Plausible site, or Matomo site).\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsProperty.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"displayName\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The name people see for this website or app.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name people see for this website or app.\\\"}},\\\"provider\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Which analytics service tracks it: Google Analytics 4, Plausible or Matomo.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Which analytics service tracks it: Google Analytics 4, Plausible or Matomo.\\\"},\\\"enum\\\":[\\\"ga4\\\",\\\"plausible\\\",\\\"matomo\\\"]},\\\"externalId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The id the analytics service gives the property, such as properties/123456789 or a Matomo site id.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The id the analytics service gives the property, such as properties/123456789 or a Matomo site id.\\\"}},\\\"measurementId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The tracking id for a web stream, such as G-XXXXXXXXXX.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The tracking id for a web stream, such as G-XXXXXXXXXX.\\\"}},\\\"apiSecret\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The secret key your analytics service issues for this property. It is kept out of responses and tool results, but it is not encrypted in the database.\\\",\\\"_meta\\\":{\\\"sensitive\\\":true,\\\"description\\\":\\\"The secret key your analytics service issues for this property. It is kept out of responses and tool results, but it is not encrypted in the database.\\\"},\\\"sensitive\\\":true},\\\"siteDomain\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"The website address, used by Plausible and Matomo.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The website address, used by Plausible and Matomo.\\\"}},\\\"timeZone\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"America/Los_Angeles\\\",\\\"description\\\":\\\"The time zone the figures are reported in.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The time zone the figures are reported in.\\\"}},\\\"currencyCode\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"USD\\\",\\\"description\\\":\\\"The currency used for revenue figures, such as USD.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The currency used for revenue figures, such as USD.\\\"}},\\\"industryCategory\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"serviceLevel\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"STANDARD\\\"},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Whether tracking is active, inactive or still being set up.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Whether tracking is active, inactive or still being set up.\\\"},\\\"enum\\\":[\\\"active\\\",\\\"inactive\\\",\\\"pending\\\"]},\\\"lastSyncAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false,\\\"description\\\":\\\"When the property was last synced with the analytics service.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"When the property was last synced with the analytics service.\\\"}},\\\"providerMetadata\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"{}\\\",\\\"description\\\":\\\"Extra connection details from your analytics service. They are kept out of responses and tool results, but they are not encrypted in the database.\\\",\\\"_meta\\\":{\\\"sensitive\\\":true,\\\"description\\\":\\\"Extra connection details from your analytics service. They are kept out of responses and tool results, but they are not encrypted in the database.\\\"},\\\"sensitive\\\":true}},\\\"methods\\\":{\\\"isGA4\\\":{\\\"name\\\":\\\"isGA4\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isPlausible\\\":{\\\"name\\\":\\\"isPlausible\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isMatomo\\\":{\\\"name\\\":\\\"isMatomo\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getProviderMetadata\\\":{\\\"name\\\":\\\"getProviderMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setProviderMetadata\\\":{\\\"name\\\":\\\"setProviderMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metadata\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markSynced\\\":{\\\"name\\\":\\\"markSynced\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"analyzePerformance\\\":{\\\"name\\\":\\\"analyzePerformance\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"object\\\",\\\"optional\\\":true,\\\"memberTypes\\\":[\\\"string\\\"]}],\\\"returnType\\\":\\\"Promise<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isPerformingWell\\\":{\\\"name\\\":\\\"isPerformingWell\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"sync\\\",\\\"runReport\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsProperty\\\",\\\"collectionExportName\\\":\\\"AnalyticsPropertyCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_properties\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_properties\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"display_name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"provider\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"measurement_id\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"api_secret\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"site_domain\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"time_zone\\\\\\\" TEXT DEFAULT 'America/Los_Angeles',\\\\n \\\\\\\"currency_code\\\\\\\" TEXT DEFAULT 'USD',\\\\n \\\\\\\"industry_category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"service_level\\\\\\\" TEXT DEFAULT 'STANDARD',\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"last_sync_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"provider_metadata\\\\\\\" TEXT DEFAULT '{}'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"display_name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"provider\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"measurement_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"api_secret\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"site_domain\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"time_zone\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"America/Los_Angeles\\\"},\\\"currency_code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"USD\\\"},\\\"industry_category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"service_level\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"STANDARD\\\"},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_sync_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"provider_metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"{}\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_properties_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_properties_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_properties_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"f38a20b9\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"},\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\":{\\\"name\\\":\\\"analyticsreport\\\",\\\"className\\\":\\\"AnalyticsReport\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\",\\\"collection\\\":\\\"analyticsreports\\\",\\\"description\\\":\\\"AnalyticsReport represents a saved report configuration with optional scheduling.\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/analytics/src/models/AnalyticsReport.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-analytics\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"propertyId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"AnalyticsProperty\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The website or app this report is about.\\\"}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"A short name for the report, such as Weekly traffic.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A short name for the report, such as Weekly traffic.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"What the report is for.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"What the report is for.\\\"}},\\\"dimensions\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"[]\\\",\\\"description\\\":\\\"What to break the figures down by, such as country or device, as a list.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"What to break the figures down by, such as country or device, as a list.\\\"}},\\\"metrics\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"[]\\\",\\\"description\\\":\\\"What to count, such as active users or sessions, as a list.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"What to count, such as active users or sessions, as a list.\\\"}},\\\"dateRangeStart\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"7daysAgo\\\",\\\"description\\\":\\\"Where the period begins: a date, or a relative value such as 7daysAgo.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the period begins: a date, or a relative value such as 7daysAgo.\\\"}},\\\"dateRangeEnd\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"today\\\",\\\"description\\\":\\\"Where the period ends: a date, or a relative value such as today.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the period ends: a date, or a relative value such as today.\\\"}},\\\"dimensionFilter\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"metricFilter\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"orderBy\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"maxResults\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Where the report stands: draft, scheduled, running, completed or failed.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the report stands: draft, scheduled, running, completed or failed.\\\"},\\\"enum\\\":[\\\"draft\\\",\\\"scheduled\\\",\\\"running\\\",\\\"completed\\\",\\\"failed\\\"]},\\\"frequency\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"How often the report repeats: once, daily, weekly or monthly.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"How often the report repeats: once, daily, weekly or monthly.\\\"},\\\"enum\\\":[\\\"once\\\",\\\"daily\\\",\\\"weekly\\\",\\\"monthly\\\"]},\\\"lastRunAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false,\\\"description\\\":\\\"When the report last ran.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"When the report last ran.\\\"}},\\\"nextRunAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false,\\\"description\\\":\\\"When the report is next due to run.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"When the report is next due to run.\\\"}},\\\"resultData\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"rowCount\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0,\\\"description\\\":\\\"How many rows the last run returned.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"How many rows the last run returned.\\\"}},\\\"lastError\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\",\\\"description\\\":\\\"Why the last run failed. Empty when it worked.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Why the last run failed. Empty when it worked.\\\"}}},\\\"methods\\\":{\\\"getDimensions\\\":{\\\"name\\\":\\\"getDimensions\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Array<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setDimensions\\\":{\\\"name\\\":\\\"setDimensions\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"dimensions\\\",\\\"type\\\":\\\"Array<object>\\\",\\\"optional\\\":false,\\\"memberTypes\\\":[\\\"string\\\"]}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getMetrics\\\":{\\\"name\\\":\\\"getMetrics\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Array<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setMetrics\\\":{\\\"name\\\":\\\"setMetrics\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metrics\\\",\\\"type\\\":\\\"Array<object>\\\",\\\"optional\\\":false,\\\"memberTypes\\\":[\\\"string\\\"]}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getResultData\\\":{\\\"name\\\":\\\"getResultData\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown> | null\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setResultData\\\":{\\\"name\\\":\\\"setResultData\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"data\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markRunning\\\":{\\\"name\\\":\\\"markRunning\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markCompleted\\\":{\\\"name\\\":\\\"markCompleted\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"rowCount\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"markFailed\\\":{\\\"name\\\":\\\"markFailed\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"error\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"calculateNextRun\\\":{\\\"name\\\":\\\"calculateNextRun\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"isDue\\\":{\\\"name\\\":\\\"isDue\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"boolean\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"analyzeResults\\\":{\\\"name\\\":\\\"analyzeResults\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_options\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<object>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"hasPositiveTrends\\\":{\\\"name\\\":\\\"hasPositiveTrends\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AnalyticsReport\\\",\\\"collectionExportName\\\":\\\"AnalyticsReportCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"analytics_reports\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"analytics_reports\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"property_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"description\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"dimensions\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"metrics\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"date_range_start\\\\\\\" TEXT DEFAULT '7daysAgo',\\\\n \\\\\\\"date_range_end\\\\\\\" TEXT DEFAULT 'today',\\\\n \\\\\\\"dimension_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"metric_filter\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"order_by\\\\\\\" TEXT DEFAULT '[]',\\\\n \\\\\\\"max_results\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"status\\\\\\\" TEXT,\\\\n \\\\\\\"frequency\\\\\\\" TEXT,\\\\n \\\\\\\"last_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"next_run_at\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"result_data\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"row_count\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"last_error\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"property_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"analytics_properties\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"dimensions\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"metrics\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"date_range_start\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"7daysAgo\\\"},\\\"date_range_end\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"today\\\"},\\\"dimension_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"metric_filter\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"order_by\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"[]\\\"},\\\"max_results\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"frequency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"last_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"next_run_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false},\\\"result_data\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"},\\\"row_count\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"last_error\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"analytics_reports_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"analytics_reports_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"analytics_reports_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"analytics_reports_property_id_idx\\\",\\\"columns\\\":[\\\"property_id\\\"]}],\\\"version\\\":\\\"22ce0f1c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-analytics\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"recipes\\\":[{\\\"id\\\":\\\"analytics.reports\\\",\\\"className\\\":\\\"AnalyticsReportsRecipe\\\",\\\"label\\\":\\\"Analytics\\\",\\\"summary\\\":\\\"Save traffic reports for your websites and apps and get a plain-language read on the results.\\\",\\\"synonyms\\\":[\\\"analytics\\\",\\\"traffic\\\",\\\"pageviews\\\",\\\"visitors\\\",\\\"web stats\\\",\\\"google analytics\\\",\\\"plausible\\\",\\\"matomo\\\"],\\\"models\\\":[\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\",\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\"],\\\"nav\\\":[{\\\"label\\\":\\\"Analytics properties\\\",\\\"model\\\":\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\",\\\"icon\\\":\\\"home\\\",\\\"description\\\":\\\"The websites and apps you track, and who tracks them.\\\"},{\\\"label\\\":\\\"Analytics reports\\\",\\\"model\\\":\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\",\\\"icon\\\":\\\"fileText\\\",\\\"description\\\":\\\"Saved reports, how often they run and what they found.\\\"}],\\\"requires\\\":[],\\\"section\\\":{\\\"id\\\":\\\"analytics\\\",\\\"label\\\":\\\"Analytics\\\",\\\"icon\\\":\\\"layers\\\",\\\"description\\\":\\\"How your websites and apps are doing.\\\"},\\\"options\\\":{\\\"@happyvertical/smrt-analytics:AnalyticsProperty\\\":{\\\"fields\\\":{\\\"name\\\":{\\\"visibility\\\":\\\"hidden\\\"},\\\"industryCategory\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"serviceLevel\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"lastSyncAt\\\":{\\\"locked\\\":true}}},\\\"@happyvertical/smrt-analytics:AnalyticsReport\\\":{\\\"fields\\\":{\\\"dimensionFilter\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"metricFilter\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"orderBy\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"maxResults\\\":{\\\"visibility\\\":\\\"advanced\\\"},\\\"resultData\\\":{\\\"visibility\\\":\\\"hidden\\\",\\\"locked\\\":true},\\\"rowCount\\\":{\\\"locked\\\":true},\\\"lastRunAt\\\":{\\\"locked\\\":true},\\\"nextRunAt\\\":{\\\"locked\\\":true},\\\"lastError\\\":{\\\"locked\\\":true}}}},\\\"help\\\":{\\\"markdown\\\":\\\"## Overview\\\\n\\\\nAnalytics keeps the traffic reports you care about for your websites and apps.\\\\nAdd a property for each site or app you track in Google Analytics 4, Plausible\\\\nor Matomo. Save a report on it that says what to count, what to break the\\\\nfigures down by, and over which dates. The rows each run returns are stored\\\\nwith the report, and the assistant can read them back to you in plain words.\\\\n\\\\nProperties and reports are kept on your server. Provider secrets are stored on\\\\nthe property and never sent to the assistant or returned in responses, but they\\\\nare not encrypted in the database.\\\\n\\\\n## Tasks\\\\n\\\\n### Add a property\\\\n\\\\n1. Create a new property.\\\\n2. Enter the **{field:AnalyticsProperty.displayName}** and pick the\\\\n **{field:AnalyticsProperty.provider}**.\\\\n3. Enter the **{field:AnalyticsProperty.externalId}** the analytics service\\\\n gives it.\\\\n4. For Google Analytics, enter the **{field:AnalyticsProperty.measurementId}**.\\\\n For Plausible or Matomo, enter the **{field:AnalyticsProperty.siteDomain}**.\\\\n5. Keep **{field:AnalyticsProperty.status}** on active while you want it\\\\n tracked.\\\\n\\\\n### Save a report\\\\n\\\\n1. Create a new report and choose the property in\\\\n **{field:AnalyticsReport.propertyId}**.\\\\n2. Give it a **{field:AnalyticsReport.name}**.\\\\n3. List what to break the figures down by in\\\\n **{field:AnalyticsReport.dimensions}** and what to count in\\\\n **{field:AnalyticsReport.metrics}**.\\\\n4. Set the period with **{field:AnalyticsReport.dateRangeStart}** and\\\\n **{field:AnalyticsReport.dateRangeEnd}**.\\\\n5. Pick **{field:AnalyticsReport.frequency}** to repeat it daily, weekly or\\\\n monthly.\\\\n\\\\n### Check what a report found\\\\n\\\\n1. **{field:AnalyticsReport.status}** says where the report stands, and\\\\n **{field:AnalyticsReport.lastRunAt}** when it last ran.\\\\n2. **{field:AnalyticsReport.rowCount}** shows how many rows came back.\\\\n3. If a run failed, **{field:AnalyticsReport.lastError}** says why.\\\\n\\\\n### Ask the assistant\\\\n\\\\n1. Ask about a saved report by name, for example \\\\\\\"how did last week's traffic\\\\n report look?\\\\\\\". When your app has the analytics tools switched on, the\\\\n assistant can list your saved reports and open one to read its figures.\\\\n2. A written summary of a report comes from your app, which sends the stored\\\\n rows to the AI provider as they are. Leave out dimensions that identify a\\\\n person.\\\\n3. For totals your own app calculates, such as orders or signups, ask for a\\\\n refreshed report. The assistant reads those with `reports.query`; see\\\\n Reports.\\\\n4. For a one-off question those reports do not answer, the assistant can\\\\n propose a report with `reports.runtime.define`. Nothing is saved until you\\\\n confirm it.\\\\n\\\",\\\"fieldRefs\\\":[\\\"AnalyticsProperty.displayName\\\",\\\"AnalyticsProperty.externalId\\\",\\\"AnalyticsProperty.measurementId\\\",\\\"AnalyticsProperty.provider\\\",\\\"AnalyticsProperty.siteDomain\\\",\\\"AnalyticsProperty.status\\\",\\\"AnalyticsReport.dateRangeEnd\\\",\\\"AnalyticsReport.dateRangeStart\\\",\\\"AnalyticsReport.dimensions\\\",\\\"AnalyticsReport.frequency\\\",\\\"AnalyticsReport.lastError\\\",\\\"AnalyticsReport.lastRunAt\\\",\\\"AnalyticsReport.metrics\\\",\\\"AnalyticsReport.name\\\",\\\"AnalyticsReport.propertyId\\\",\\\"AnalyticsReport.rowCount\\\",\\\"AnalyticsReport.status\\\"]},\\\"surfaces\\\":[{\\\"kind\\\":\\\"route\\\",\\\"path\\\":\\\"/analytics/summary\\\",\\\"export\\\":\\\"@happyvertical/smrt-analytics/svelte#AnalyticsSummary\\\",\\\"label\\\":\\\"Traffic summary\\\"}],\\\"runtime\\\":\\\"server\\\",\\\"demo\\\":{\\\"mode\\\":\\\"server\\\",\\\"reasons\\\":[\\\"The recipe declares runtime \\\\\\\"server\\\\\\\".\\\",\\\"Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624).\\\"]}}],\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\",\\\"@happyvertical/smrt-prompts\\\",\\\"@happyvertical/smrt-tenancy\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3624\\\"],\\\"via\\\":[\\\"@happyvertical/smrt-tenancy\\\"]},\\\"stampsConstructors\\\":true}\"));\n","/**\n * Prompt registrations for the @happyvertical/smrt-analytics package.\n *\n * Prompts are registered at module-load time via `definePrompt()` so that\n * tenant-aware overrides can be applied at call time via `resolvePrompt()`.\n *\n * Mirrors the pattern used by `@happyvertical/smrt-properties` (see\n * `prompts.ts`) and `@happyvertical/smrt-content` (see `content-prompts.ts`).\n *\n * PII / internal-field exclusion policy:\n *\n * Variables that this package never forwards to the AI provider:\n * - `id`, `tenantId`, `propertyId` and other foreign-key/UUID fields —\n * they identify internal records and provide no analytic value.\n * - `apiSecret`, `measurementId`, `externalId`, `siteDomain` — these are\n * provider credentials or platform-specific identifiers (e.g. GA4 API\n * secrets, Matomo `idSite`, custom `G-XXXX` measurement IDs) that may\n * be tenant-private configuration.\n * - `providerMetadata` — extensible JSON blob that may contain\n * credentials, account IDs, or other configuration secrets.\n * - `lastError`, raw `dimensionFilter` / `metricFilter` JSON — internal\n * error strings (which may contain auth tokens) and filter expressions\n * that may reference cookie IDs or user-pseudo-IDs are kept out of the\n * prompt variable set.\n *\n * Variables this package DOES forward (potentially carrying PII the caller\n * persisted):\n * - `reportData` (a JSON.stringify of the persisted `resultData`) — this\n * is forwarded VERBATIM to the AI in `analyzeResults` and\n * `hasPositiveTrends`. The package cannot strip PII because the row\n * schema is determined by which dimensions/metrics the caller asked\n * the analytics provider to return. If the persisted rows contain\n * `userPseudoId`, `clientId`, IP-derived geolocation, or any other\n * identifier, those fields WILL reach the AI provider. The forwarding\n * is pinned by a regression test in\n * `__tests__/analytics-report-prompt.test.ts`. Callers must either\n * exclude PII-bearing dimensions before persisting, apply a column\n * allowlist at the call site, or override the prompt template via\n * `PromptOverride` to redact rows.\n *\n * Acceptable variables (always safe): human-readable names, time periods,\n * computed aggregates (row counts, totals), and high-level dimension/metric\n * labels (which are public GA4/Plausible/Matomo schema names).\n */\n\nimport {\n definePrompt,\n type ResolvedPromptAI,\n} from '@happyvertical/smrt-prompts';\n\n/**\n * `AnalyticsProperty.analyzePerformance()` prompt. Only the human-readable\n * display name, provider label, and the requested period reach the model;\n * provider IDs, API secrets, and the providerMetadata blob are excluded.\n */\nexport const smrtAnalyticsAnalyzePerformancePrompt = definePrompt({\n key: 'smrtAnalytics.property.analyzePerformance',\n template: `Analyze the analytics performance for this property over the last {period}.\nConsider: traffic trends, user engagement, conversion patterns.\nProperty: {propertyDisplayName} ({propertyProvider})`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\n/**\n * `AnalyticsReport.analyzeResults()` prompt. Surfaces report name, the\n * dimension/metric labels (which are public GA4/Plausible/Matomo schema\n * names — not user data), the date-range window, and the row count plus\n * a JSON-stringified `resultData` blob.\n *\n * **`resultData` is forwarded verbatim.** This package does not strip PII\n * from result rows — it cannot, because the row schema is determined by\n * the dimensions/metrics the caller asked the analytics provider to\n * return. If the caller persists rows containing a `userPseudoId`,\n * `clientId`, IP-derived geolocation, or any other identifier, those\n * fields WILL reach the AI provider. Either:\n *\n * - exclude PII-bearing dimensions before persisting `resultData`\n * (e.g. don't request `userPseudoId` as a GA4 dimension), or\n * - apply a column allowlist at the call site before invoking\n * `analyzeResults()`, or\n * - override the prompt template via `PromptOverride` to redact rows.\n *\n * The forwarding contract is pinned by a regression test in\n * `__tests__/analytics-report-prompt.test.ts`.\n */\nexport const smrtAnalyticsAnalyzeResultsPrompt = definePrompt({\n key: 'smrtAnalytics.report.analyzeResults',\n template: `Analyze these analytics report results and provide insights:\n\nReport: {reportName}\nDimensions: {reportDimensions}\nMetrics: {reportMetrics}\nDate Range: {dateRangeStart} to {dateRangeEnd}\nRow Count: {rowCount}\n\nData: {reportData}\n\nProvide:\n1. Key findings\n2. Trends or patterns\n3. Actionable recommendations`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\n/**\n * `AnalyticsReport.hasPositiveTrends()` boolean classifier prompt. Same\n * variables as `analyzeResults` minus the descriptive name — only the\n * provider-schema metric labels and the aggregate `resultData` payload are\n * needed to decide whether the trend is positive.\n *\n * The template explicitly instructs the model to begin its response with\n * either \"yes\" or \"no\" so the boolean coercion in `hasPositiveTrends()`\n * (`/^\\s*(yes|true)\\b/i`) is reliable. Without the leading-word\n * instruction the model commonly answers with prose that happens to\n * describe positive trends but starts with a sentence like \"The\n * conversion rate is up...\" — the parser would then treat that as\n * `false` despite a positive analysis. Tenants overriding this template\n * MUST preserve the leading yes/no convention.\n */\nexport const smrtAnalyticsHasPositiveTrendsPrompt = definePrompt({\n key: 'smrtAnalytics.report.hasPositiveTrends',\n template: `Based on these report results, are the metrics showing positive trends?\n\nMetrics: {reportMetrics}\nData: {reportData}\n\nConsider: user growth, engagement, conversions as positive indicators.\n\nBegin your response with the single word \"yes\" or \"no\" (lower case), then\noptionally provide a one-sentence explanation. Tooling parses the leading\nword — \"yes\" or \"true\" means positive, anything else means negative.`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\n/**\n * Build the message-options object passed to `aiClient.message()` from a\n * `ResolvedPromptAI` shape. Mirrors the helper in `smrt-properties` and\n * `smrt-content` so behavior across packages is identical.\n */\nexport function promptMessageOptions(ai: ResolvedPromptAI) {\n return {\n ...(ai.params || {}),\n ...(ai.model ? { model: ai.model } : {}),\n ...(typeof ai.temperature === 'number'\n ? { temperature: ai.temperature }\n : {}),\n ...(typeof ai.maxTokens === 'number' ? { maxTokens: ai.maxTokens } : {}),\n };\n}\n","/**\n * Types and enums for smrt-analytics package\n */\n\n/**\n * Analytics provider types\n */\nexport enum AnalyticsProvider {\n GA4 = 'ga4',\n PLAUSIBLE = 'plausible',\n MATOMO = 'matomo',\n}\n\n/**\n * Status for analytics properties\n */\nexport enum AnalyticsPropertyStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n PENDING = 'pending',\n}\n\n/**\n * Data stream types (matching SDK types)\n */\nexport enum DataStreamType {\n WEB = 'WEB_DATA_STREAM',\n ANDROID = 'ANDROID_APP_DATA_STREAM',\n IOS = 'IOS_APP_DATA_STREAM',\n}\n\n/**\n * Status for data streams\n */\nexport enum DataStreamStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n}\n\n/**\n * Report status\n */\nexport enum ReportStatus {\n DRAFT = 'draft',\n SCHEDULED = 'scheduled',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n}\n\n/**\n * Report frequency for scheduled reports\n */\nexport enum ReportFrequency {\n ONCE = 'once',\n DAILY = 'daily',\n WEEKLY = 'weekly',\n MONTHLY = 'monthly',\n}\n\n/**\n * Custom dimension scope (matching SDK types)\n */\nexport enum CustomDimensionScope {\n EVENT = 'EVENT',\n USER = 'USER',\n ITEM = 'ITEM',\n}\n\n/**\n * Event tracking status\n */\nexport enum TrackingEventStatus {\n PENDING = 'pending',\n SENT = 'sent',\n FAILED = 'failed',\n}\n\n/**\n * Key event counting method (matching SDK types)\n */\nexport enum CountingMethod {\n ONCE_PER_EVENT = 'ONCE_PER_EVENT',\n ONCE_PER_SESSION = 'ONCE_PER_SESSION',\n}\n\n// ============================================================================\n// Analytics aggregation types\n// ============================================================================\n\n/**\n * Day-over-day property stats with trend calculation\n */\nexport interface PropertyStatsWithTrend {\n todayPageviews: number;\n todayUsers: number;\n yesterdayPageviews: number;\n yesterdayUsers: number;\n trend: 'up' | 'down' | 'flat';\n /**\n * Day-over-day percentage change. `null` when today grew from a zero\n * yesterday-baseline (a \"new\" surge with no finite percentage); the UI\n * renders this as \"new\" rather than 0%.\n */\n trendPercent: number | null;\n}\n\n// ============================================================================\n// SDK-compatible type definitions\n// These mirror the types from @happyvertical/analytics for when SDK is not installed\n// ============================================================================\n\n/**\n * Analytics property/site representation (SDK-compatible)\n */\nexport interface SDKProperty {\n id: string;\n name: string;\n displayName: string;\n createTime: string;\n updateTime?: string;\n timeZone?: string;\n currencyCode?: string;\n industryCategory?: string;\n serviceLevel?: 'STANDARD' | 'PREMIUM';\n}\n\n/**\n * Data stream representation (SDK-compatible)\n */\nexport interface SDKDataStream {\n id: string;\n type: 'WEB_DATA_STREAM' | 'ANDROID_APP_DATA_STREAM' | 'IOS_APP_DATA_STREAM';\n displayName: string;\n measurementId?: string;\n firebaseAppId?: string;\n defaultUri?: string;\n createTime: string;\n updateTime?: string;\n}\n\n/**\n * Custom dimension representation (SDK-compatible)\n */\nexport interface SDKCustomDimension {\n id: string;\n name: string;\n parameterName: string;\n displayName: string;\n description?: string;\n scope: 'EVENT' | 'USER' | 'ITEM';\n disallowAdsPersonalization?: boolean;\n}\n\n/**\n * Custom metric representation (SDK-compatible)\n */\nexport interface SDKCustomMetric {\n id: string;\n name: string;\n parameterName: string;\n displayName: string;\n description?: string;\n scope: 'EVENT';\n measurementUnit: string;\n restrictedMetricType?: 'COST_DATA' | 'REVENUE_DATA';\n}\n\n/**\n * Key event (conversion) representation (SDK-compatible)\n */\nexport interface SDKKeyEvent {\n id: string;\n name: string;\n eventName: string;\n createTime: string;\n countingMethod?: 'ONCE_PER_EVENT' | 'ONCE_PER_SESSION';\n defaultValue?: {\n numericValue?: number;\n currencyCode?: string;\n };\n}\n\n/**\n * Report options (SDK-compatible)\n */\nexport interface SDKReportOptions {\n dateRanges: Array<{\n startDate: string;\n endDate: string;\n name?: string;\n }>;\n dimensions?: Array<{ name: string }>;\n metrics: Array<{ name: string }>;\n offset?: number;\n limit?: number;\n orderBys?: Array<{\n metric?: { metricName: string };\n dimension?: { dimensionName: string };\n desc?: boolean;\n }>;\n keepEmptyRows?: boolean;\n returnPropertyQuota?: boolean;\n}\n\n/**\n * Report result (SDK-compatible)\n */\nexport interface SDKReportResult {\n dimensionHeaders: Array<{ name: string }>;\n metricHeaders: Array<{ name: string; type: string }>;\n rows: Array<{\n dimensionValues: Array<{ value: string }>;\n metricValues: Array<{ value: string }>;\n }>;\n rowCount?: number;\n metadata?: {\n currencyCode?: string;\n timeZone?: string;\n dataLossFromOtherRow?: boolean;\n emptyReason?: string;\n };\n}\n\n/**\n * Track event payload (SDK-compatible)\n */\nexport interface SDKTrackEvent {\n name: string;\n params?: Record<string, string | number | boolean>;\n clientId?: string;\n userId?: string;\n timestamp?: number;\n nonPersonalizedAds?: boolean;\n}\n\n/**\n * Pageview event payload (SDK-compatible)\n */\nexport interface SDKPageviewEvent {\n pagePath: string;\n pageTitle?: string;\n pageLocation?: string;\n clientId?: string;\n userId?: string;\n params?: Record<string, string | number | boolean>;\n}\n\n/**\n * Analytics provider capabilities (SDK-compatible)\n */\nexport interface AnalyticsCapabilities {\n propertyManagement: boolean;\n dataStreams: boolean;\n customDimensions: boolean;\n customMetrics: boolean;\n keyEvents: boolean;\n reporting: boolean;\n realtimeReporting: boolean;\n serverSideTracking: boolean;\n clientSideSnippet: boolean;\n userIdentification: boolean;\n batchTracking: boolean;\n}\n\n/**\n * Analytics interface (SDK-compatible)\n * This is a subset of the full SDK interface for type compatibility\n */\nexport interface AnalyticsInterface {\n createProperty(options: {\n displayName: string;\n timeZone?: string;\n currencyCode?: string;\n }): Promise<SDKProperty>;\n listProperties(): Promise<SDKProperty[]>;\n getProperty(propertyId: string): Promise<SDKProperty>;\n getDataStreams(propertyId: string): Promise<SDKDataStream[]>;\n runReport(\n propertyId: string,\n options: SDKReportOptions,\n ): Promise<SDKReportResult>;\n track(event: SDKTrackEvent): Promise<void>;\n trackPageview(pageview: SDKPageviewEvent): Promise<void>;\n trackBatch(events: SDKTrackEvent[]): Promise<void>;\n getCapabilities(): Promise<AnalyticsCapabilities>;\n}\n","/**\n * AnalyticsDataStream model - Represents a data stream for a property\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { DataStreamStatus, DataStreamType } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsDataStream}.\n */\nexport interface AnalyticsDataStreamOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n propertyId?: string;\n displayName?: string;\n streamType?: DataStreamType;\n externalId?: string;\n measurementId?: string;\n firebaseAppId?: string;\n defaultUri?: string;\n bundleId?: string;\n packageName?: string;\n status?: DataStreamStatus;\n enhancedMeasurement?: boolean;\n}\n\n/**\n * AnalyticsDataStream represents a data stream (web, iOS, Android) for an analytics property.\n *\n * @example\n * ```typescript\n * const stream = await streams.create({\n * propertyId: property.id,\n * displayName: 'Web Stream',\n * streamType: DataStreamType.WEB,\n * measurementId: 'G-XXXXXXXXXX',\n * defaultUri: 'https://example.com'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsDataStream extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Data streams expose tenant-private platform configuration (measurement\n * IDs, Firebase app IDs, bundle/package names, default URIs). Without tenant\n * scoping the generated `list`/`get` API returns every tenant's streams.\n * `@TenantScoped` auto-filters reads and binds writes to the active tenant.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent property ID (references AnalyticsProperty)\n */\n @foreignKey('AnalyticsProperty')\n propertyId: string = '';\n\n /**\n * Human-readable display name\n */\n displayName: string = '';\n\n /**\n * Stream type (WEB, ANDROID, IOS)\n */\n streamType: DataStreamType = DataStreamType.WEB;\n\n /**\n * External stream ID from provider\n */\n externalId: string = '';\n\n /**\n * Measurement ID for web streams (G-XXXXXXX)\n */\n measurementId: string = '';\n\n /**\n * Firebase App ID for app streams\n */\n firebaseAppId: string = '';\n\n /**\n * Default URI for web streams\n */\n defaultUri: string = '';\n\n /**\n * Bundle ID for iOS apps\n */\n bundleId: string = '';\n\n /**\n * Package name for Android apps\n */\n packageName: string = '';\n\n /**\n * Stream status\n */\n status: DataStreamStatus = DataStreamStatus.ACTIVE;\n\n /**\n * Enhanced measurement enabled\n */\n enhancedMeasurement: boolean = true;\n\n constructor(options: AnalyticsDataStreamOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.propertyId !== undefined) this.propertyId = options.propertyId;\n if (options.displayName !== undefined)\n this.displayName = options.displayName;\n if (options.streamType !== undefined) this.streamType = options.streamType;\n if (options.externalId !== undefined) this.externalId = options.externalId;\n if (options.measurementId !== undefined)\n this.measurementId = options.measurementId;\n if (options.firebaseAppId !== undefined)\n this.firebaseAppId = options.firebaseAppId;\n if (options.defaultUri !== undefined) this.defaultUri = options.defaultUri;\n if (options.bundleId !== undefined) this.bundleId = options.bundleId;\n if (options.packageName !== undefined)\n this.packageName = options.packageName;\n if (options.status !== undefined) this.status = options.status;\n if (options.enhancedMeasurement !== undefined)\n this.enhancedMeasurement = options.enhancedMeasurement;\n }\n\n /**\n * Check if this is a web stream\n */\n isWeb(): boolean {\n return this.streamType === DataStreamType.WEB;\n }\n\n /**\n * Check if this is an iOS app stream\n */\n isIOS(): boolean {\n return this.streamType === DataStreamType.IOS;\n }\n\n /**\n * Check if this is an Android app stream\n */\n isAndroid(): boolean {\n return this.streamType === DataStreamType.ANDROID;\n }\n\n /**\n * Check if this is a mobile app stream (iOS or Android)\n */\n isMobileApp(): boolean {\n return this.isIOS() || this.isAndroid();\n }\n\n /**\n * Get the platform identifier (measurementId for web, firebaseAppId for apps)\n */\n getPlatformId(): string {\n if (this.isWeb()) {\n return this.measurementId;\n }\n return this.firebaseAppId;\n }\n}\n\nexport default AnalyticsDataStream;\n","/**\n * AnalyticsDataStreamCollection - Collection manager for AnalyticsDataStream objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsDataStream } from '../models/AnalyticsDataStream.js';\nimport { DataStreamStatus, DataStreamType } from '../types/index.js';\n\nexport class AnalyticsDataStreamCollection extends SmrtCollection<AnalyticsDataStream> {\n static readonly _itemClass = AnalyticsDataStream;\n\n /**\n * Find streams by property\n *\n * @param propertyId - Parent property ID\n * @returns Array of data streams\n */\n async findByProperty(propertyId: string): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: { propertyId },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find stream by external ID\n *\n * @param externalId - External stream ID from provider\n * @returns Matching stream or null\n */\n async findByExternalId(\n externalId: string,\n ): Promise<AnalyticsDataStream | null> {\n const results = await this.list({\n where: { externalId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find stream by measurement ID (web streams)\n *\n * @param measurementId - GA4 measurement ID (G-XXXXXXX)\n * @returns Matching stream or null\n */\n async findByMeasurementId(\n measurementId: string,\n ): Promise<AnalyticsDataStream | null> {\n const results = await this.list({\n where: { measurementId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find streams by type\n *\n * @param streamType - Stream type (WEB, ANDROID, IOS)\n * @returns Array of matching streams\n */\n async findByType(streamType: DataStreamType): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: { streamType },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all web streams\n */\n async findWebStreams(): Promise<AnalyticsDataStream[]> {\n return await this.findByType(DataStreamType.WEB);\n }\n\n /**\n * Find all iOS app streams\n */\n async findIOSStreams(): Promise<AnalyticsDataStream[]> {\n return await this.findByType(DataStreamType.IOS);\n }\n\n /**\n * Find all Android app streams\n */\n async findAndroidStreams(): Promise<AnalyticsDataStream[]> {\n return await this.findByType(DataStreamType.ANDROID);\n }\n\n /**\n * Find all mobile app streams (iOS + Android)\n */\n async findMobileStreams(): Promise<AnalyticsDataStream[]> {\n const ios = await this.findIOSStreams();\n const android = await this.findAndroidStreams();\n return [...ios, ...android].sort((a, b) =>\n a.displayName.localeCompare(b.displayName),\n );\n }\n\n /**\n * Find streams by status\n *\n * @param status - Stream status\n * @returns Array of matching streams\n */\n async findByStatus(status: DataStreamStatus): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: { status },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all active streams\n */\n async findActive(): Promise<AnalyticsDataStream[]> {\n return await this.findByStatus(DataStreamStatus.ACTIVE);\n }\n\n /**\n * Find active streams for a property\n *\n * @param propertyId - Parent property ID\n * @returns Array of active streams\n */\n async findActiveByProperty(\n propertyId: string,\n ): Promise<AnalyticsDataStream[]> {\n return await this.list({\n where: {\n propertyId,\n status: DataStreamStatus.ACTIVE,\n },\n orderBy: 'displayName ASC',\n });\n }\n}\n","/**\n * AnalyticsEvent model - Tracked events log\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { TrackingEventStatus } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsEvent}.\n */\nexport interface AnalyticsEventOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n propertyId?: string;\n eventName?: string;\n clientId?: string;\n userId?: string;\n params?: string;\n eventTimestamp?: Date;\n status?: TrackingEventStatus;\n sentAt?: Date | null;\n errorMessage?: string;\n retryCount?: number;\n nonPersonalizedAds?: boolean;\n sessionId?: string;\n pagePath?: string;\n pageTitle?: string;\n userAgent?: string;\n ipAddress?: string;\n}\n\n/**\n * AnalyticsEvent represents a tracked analytics event (server-side tracking log).\n *\n * @example\n * ```typescript\n * const event = await events.create({\n * propertyId: property.id,\n * eventName: 'purchase',\n * clientId: 'user-123',\n * params: JSON.stringify({ value: 99.99, currency: 'USD' })\n * });\n * await event.send();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create'] },\n mcp: { include: ['list', 'get', 'track'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsEvent extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Analytics events carry end-user PII (`userId`, `clientId`, `ipAddress`,\n * `userAgent`, `sessionId`). Without tenant scoping the generated\n * `list`/`get` API leaks every tenant's visitor data, and `create` lets a\n * caller write events bound to another tenant's `propertyId`. `@TenantScoped`\n * auto-filters reads and binds writes to the active tenant context.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent property ID (references AnalyticsProperty)\n */\n @foreignKey('AnalyticsProperty')\n propertyId: string = '';\n\n /**\n * Event name (e.g., 'purchase', 'page_view', 'sign_up')\n */\n eventName: string = '';\n\n /**\n * Client ID for anonymous tracking\n */\n clientId: string = '';\n\n /**\n * User ID for identified tracking\n */\n userId: string = '';\n\n /**\n * Event parameters (JSON)\n */\n params: string = '{}';\n\n /**\n * Event timestamp\n */\n eventTimestamp: Date = new Date();\n\n /**\n * Tracking status\n */\n status: TrackingEventStatus = TrackingEventStatus.PENDING;\n\n /**\n * Timestamp when sent to provider\n */\n sentAt: Date | null = null;\n\n /**\n * Error message if sending failed\n */\n errorMessage: string = '';\n\n /**\n * Retry count\n */\n retryCount: number = 0;\n\n /**\n * Disable personalized ads\n */\n nonPersonalizedAds: boolean = false;\n\n /**\n * Session ID\n */\n sessionId: string = '';\n\n /**\n * Page path (for pageview events)\n */\n pagePath: string = '';\n\n /**\n * Page title (for pageview events)\n */\n pageTitle: string = '';\n\n /**\n * User agent\n */\n userAgent: string = '';\n\n /**\n * IP address (anonymized)\n */\n ipAddress: string = '';\n\n constructor(options: AnalyticsEventOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.propertyId !== undefined) this.propertyId = options.propertyId;\n if (options.eventName !== undefined) this.eventName = options.eventName;\n if (options.clientId !== undefined) this.clientId = options.clientId;\n if (options.userId !== undefined) this.userId = options.userId;\n if (options.params !== undefined) this.params = options.params;\n if (options.eventTimestamp !== undefined)\n this.eventTimestamp = options.eventTimestamp;\n if (options.status !== undefined) this.status = options.status;\n if (options.sentAt !== undefined) this.sentAt = options.sentAt;\n if (options.errorMessage !== undefined)\n this.errorMessage = options.errorMessage;\n if (options.retryCount !== undefined) this.retryCount = options.retryCount;\n if (options.nonPersonalizedAds !== undefined)\n this.nonPersonalizedAds = options.nonPersonalizedAds;\n if (options.sessionId !== undefined) this.sessionId = options.sessionId;\n if (options.pagePath !== undefined) this.pagePath = options.pagePath;\n if (options.pageTitle !== undefined) this.pageTitle = options.pageTitle;\n if (options.userAgent !== undefined) this.userAgent = options.userAgent;\n if (options.ipAddress !== undefined) this.ipAddress = options.ipAddress;\n }\n\n /**\n * Get parsed event parameters\n */\n getParams(): Record<string, string | number | boolean> {\n try {\n return JSON.parse(this.params);\n } catch {\n return {};\n }\n }\n\n /**\n * Set event parameters\n */\n setParams(params: Record<string, string | number | boolean>): void {\n this.params = JSON.stringify(params);\n }\n\n /**\n * Add a parameter to the event\n */\n addParam(key: string, value: string | number | boolean): void {\n const params = this.getParams();\n params[key] = value;\n this.setParams(params);\n }\n\n /**\n * Check if this is a pageview event\n */\n isPageview(): boolean {\n return this.eventName === 'page_view' || this.eventName === 'pageview';\n }\n\n /**\n * Check if this is a conversion event\n */\n isConversion(): boolean {\n const conversionEvents = [\n 'purchase',\n 'sign_up',\n 'generate_lead',\n 'begin_checkout',\n ];\n return conversionEvents.includes(this.eventName);\n }\n\n /**\n * Mark as sent successfully\n */\n markSent(): void {\n this.status = TrackingEventStatus.SENT;\n this.sentAt = new Date();\n this.errorMessage = '';\n }\n\n /**\n * Mark as failed\n */\n markFailed(error: string): void {\n this.status = TrackingEventStatus.FAILED;\n this.errorMessage = error;\n this.retryCount++;\n }\n\n /**\n * Reset for retry\n */\n resetForRetry(): void {\n this.status = TrackingEventStatus.PENDING;\n this.sentAt = null;\n }\n\n /**\n * Check if event should be retried\n */\n shouldRetry(maxRetries: number = 3): boolean {\n return (\n this.status === TrackingEventStatus.FAILED && this.retryCount < maxRetries\n );\n }\n\n /**\n * Build SDK TrackEvent object\n */\n toTrackEvent(): {\n name: string;\n params?: Record<string, string | number | boolean>;\n clientId?: string;\n userId?: string;\n timestamp?: number;\n nonPersonalizedAds?: boolean;\n } {\n return {\n name: this.eventName,\n params: this.getParams(),\n clientId: this.clientId || undefined,\n userId: this.userId || undefined,\n timestamp: this.eventTimestamp.getTime() * 1000, // Convert to microseconds\n nonPersonalizedAds: this.nonPersonalizedAds,\n };\n }\n}\n\nexport default AnalyticsEvent;\n","/**\n * AnalyticsEventCollection - Collection manager for AnalyticsEvent objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsEvent } from '../models/AnalyticsEvent.js';\nimport {\n type PropertyStatsWithTrend,\n TrackingEventStatus,\n} from '../types/index.js';\n\nexport class AnalyticsEventCollection extends SmrtCollection<AnalyticsEvent> {\n static readonly _itemClass = AnalyticsEvent;\n\n /**\n * Offset of `timeZone` at `instant`, in milliseconds (wall-clock minus UTC).\n *\n * Reads the zone's wall-clock Y/M/D h:m:s for `instant` via\n * `Intl.DateTimeFormat` parts and subtracts the real UTC instant. Positive\n * east of UTC, negative west (e.g. `America/Los_Angeles` returns roughly\n * `-7h`/`-8h` depending on DST).\n *\n * @throws RangeError if `timeZone` is not a valid IANA identifier.\n */\n private zoneOffsetMs(instant: Date, timeZone: string): number {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n }).formatToParts(instant);\n const lookup = (type: Intl.DateTimeFormatPartTypes): number =>\n Number.parseInt(parts.find((p) => p.type === type)?.value ?? '0', 10);\n let hour = lookup('hour');\n // Intl can emit hour \"24\" for midnight under hour12:false; normalize to 0.\n if (hour === 24) hour = 0;\n const asUtc = Date.UTC(\n lookup('year'),\n lookup('month') - 1,\n lookup('day'),\n hour,\n lookup('minute'),\n lookup('second'),\n );\n return asUtc - instant.getTime();\n }\n\n /**\n * Resolve the UTC instant marking the start of the calendar day (00:00) that\n * `instant` falls on **within the given IANA time zone**.\n *\n * Day-over-day buckets (\"today vs yesterday\") must respect the property's\n * configured `timeZone` (defaults to `America/Los_Angeles`), otherwise a\n * pageview at 11:30pm local time — already the next UTC day — is bucketed\n * into the wrong day. We read the wall-clock civil date for the zone, then\n * map that date's local midnight back to a UTC instant, correcting for the\n * zone offset (and re-correcting once across a DST boundary).\n *\n * Invalid/unknown zone identifiers fall back to UTC day boundaries (matching\n * the previous behaviour) rather than throwing.\n *\n * @param instant - Reference instant.\n * @param timeZone - IANA time zone (e.g. `America/Los_Angeles`).\n * @returns UTC `Date` for local midnight of the day `instant` is in.\n */\n protected startOfDayInZone(instant: Date, timeZone: string): Date {\n let civil: { year: number; month: number; day: number };\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(instant);\n const lookup = (type: Intl.DateTimeFormatPartTypes): number =>\n Number.parseInt(parts.find((p) => p.type === type)?.value ?? '0', 10);\n civil = {\n year: lookup('year'),\n month: lookup('month'),\n day: lookup('day'),\n };\n } catch {\n // Unknown/invalid time zone -> UTC day boundary.\n return new Date(\n Date.UTC(\n instant.getUTCFullYear(),\n instant.getUTCMonth(),\n instant.getUTCDate(),\n ),\n );\n }\n\n // Local midnight of `civil`, as a UTC instant: treat civil-midnight as if\n // UTC, then subtract the zone offset at that guess. Re-derive the offset at\n // the corrected instant and re-correct once if it changed (DST edge).\n const guess = Date.UTC(civil.year, civil.month - 1, civil.day);\n const offset = this.zoneOffsetMs(new Date(guess), timeZone);\n let utc = guess - offset;\n const offset2 = this.zoneOffsetMs(new Date(utc), timeZone);\n if (offset2 !== offset) utc = guess - offset2;\n return new Date(utc);\n }\n\n /**\n * Resolve the UTC instant for the start of the day *before* `todayStart`'s\n * local day, in `timeZone`.\n *\n * Steps back 12h from local midnight (landing safely inside the previous\n * civil day regardless of DST — a naive `- 24h` skips a day across\n * spring-forward), then re-resolves start-of-day.\n *\n * @param todayStart - Local-midnight UTC instant from {@link startOfDayInZone}.\n * @param timeZone - IANA time zone.\n * @returns UTC `Date` for local midnight of the prior calendar day.\n */\n protected startOfYesterdayInZone(todayStart: Date, timeZone: string): Date {\n return this.startOfDayInZone(\n new Date(todayStart.getTime() - 12 * 3_600_000),\n timeZone,\n );\n }\n\n /**\n * Classify a day-over-day change into a trend direction + percent.\n *\n * - `yesterday > 0`: percent = rounded delta; >5% up, <-5% down, else flat.\n * - `yesterday === 0 && today > 0`: a brand-new surge from a zero baseline —\n * classified `up` with a `null` percent (no finite percentage exists), so\n * the UI renders \"new\" rather than a misleading flat 0%.\n * - `yesterday === 0 && today === 0`: flat, 0%.\n *\n * @param today - Today's count.\n * @param yesterday - Yesterday's count.\n * @returns Trend direction and percent (null when growing from zero).\n */\n protected classifyTrend(\n today: number,\n yesterday: number,\n ): { trend: 'up' | 'down' | 'flat'; trendPercent: number | null } {\n if (yesterday > 0) {\n const change = ((today - yesterday) / yesterday) * 100;\n const trendPercent = Math.round(change);\n let trend: 'up' | 'down' | 'flat' = 'flat';\n if (change > 5) trend = 'up';\n else if (change < -5) trend = 'down';\n return { trend, trendPercent };\n }\n if (today > 0) {\n // Growth from a zero baseline: a real surge, no finite percentage.\n return { trend: 'up', trendPercent: null };\n }\n return { trend: 'flat', trendPercent: 0 };\n }\n\n /**\n * Find events by property\n *\n * @param propertyId - Parent property ID\n * @returns Array of events\n */\n async findByProperty(propertyId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { propertyId },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by event name\n *\n * @param eventName - Event name to filter by\n * @returns Array of matching events\n */\n async findByEventName(eventName: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { eventName },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by client ID\n *\n * @param clientId - Client ID\n * @returns Array of events for this client\n */\n async findByClientId(clientId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { clientId },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by user ID\n *\n * @param userId - User ID\n * @returns Array of events for this user\n */\n async findByUserId(userId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { userId },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find events by status\n *\n * @param status - Tracking event status\n * @returns Array of matching events\n */\n async findByStatus(status: TrackingEventStatus): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: { status },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find all pending events\n */\n async findPending(): Promise<AnalyticsEvent[]> {\n return await this.findByStatus(TrackingEventStatus.PENDING);\n }\n\n /**\n * Find all sent events\n */\n async findSent(): Promise<AnalyticsEvent[]> {\n return await this.findByStatus(TrackingEventStatus.SENT);\n }\n\n /**\n * Find all failed events\n */\n async findFailed(): Promise<AnalyticsEvent[]> {\n return await this.findByStatus(TrackingEventStatus.FAILED);\n }\n\n /**\n * Find events that should be retried\n *\n * @param maxRetries - Maximum retry count\n * @returns Array of events eligible for retry\n */\n async findForRetry(maxRetries: number = 3): Promise<AnalyticsEvent[]> {\n const failed = await this.findFailed();\n return failed.filter((e) => e.shouldRetry(maxRetries));\n }\n\n /**\n * Find pending events for a property\n *\n * @param propertyId - Parent property ID\n * @returns Array of pending events\n */\n async findPendingByProperty(propertyId: string): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: {\n propertyId,\n status: TrackingEventStatus.PENDING,\n },\n orderBy: 'eventTimestamp ASC', // Process oldest first\n });\n }\n\n /**\n * Find events by date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of events in date range\n */\n async findByDateRange(\n startDate: Date,\n endDate: Date,\n ): Promise<AnalyticsEvent[]> {\n return await this.list({\n where: {\n 'eventTimestamp >=': startDate.toISOString(),\n 'eventTimestamp <=': endDate.toISOString(),\n },\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Find conversion events\n *\n * @param propertyId - Optional property ID filter\n * @returns Array of conversion events\n */\n async findConversions(propertyId?: string): Promise<AnalyticsEvent[]> {\n const conversionEvents = [\n 'purchase',\n 'sign_up',\n 'generate_lead',\n 'begin_checkout',\n ];\n const all = propertyId\n ? await this.findByProperty(propertyId)\n : await this.list({ orderBy: 'eventTimestamp DESC' });\n\n return all.filter((e) => conversionEvents.includes(e.eventName));\n }\n\n /**\n * Find pageview events\n *\n * @param propertyId - Optional property ID filter\n * @returns Array of pageview events\n */\n async findPageviews(propertyId?: string): Promise<AnalyticsEvent[]> {\n const where: Record<string, unknown> = { eventName: 'page_view' };\n if (propertyId) {\n where.propertyId = propertyId;\n }\n return await this.list({\n where,\n orderBy: 'eventTimestamp DESC',\n });\n }\n\n /**\n * Count events by event name for a property\n *\n * @param propertyId - Property ID\n * @returns Map of event name to count\n */\n async countByEventName(propertyId: string): Promise<Map<string, number>> {\n const events = await this.findByProperty(propertyId);\n const counts = new Map<string, number>();\n\n for (const event of events) {\n const current = counts.get(event.eventName) || 0;\n counts.set(event.eventName, current + 1);\n }\n\n return counts;\n }\n\n /**\n * Get event stats for a property\n *\n * @param propertyId - Property ID\n * @returns Event statistics\n */\n async getPropertyStats(propertyId: string): Promise<{\n total: number;\n pending: number;\n sent: number;\n failed: number;\n conversions: number;\n pageviews: number;\n }> {\n const events = await this.findByProperty(propertyId);\n\n return {\n total: events.length,\n pending: events.filter((e) => e.status === TrackingEventStatus.PENDING)\n .length,\n sent: events.filter((e) => e.status === TrackingEventStatus.SENT).length,\n failed: events.filter((e) => e.status === TrackingEventStatus.FAILED)\n .length,\n conversions: events.filter((e) => e.isConversion()).length,\n pageviews: events.filter((e) => e.isPageview()).length,\n };\n }\n\n /**\n * Get day-over-day pageview stats with trend for a property.\n *\n * Compares today's pageview count against yesterday's to produce a\n * trend direction and percentage change. A threshold of 5% is used\n * to classify 'up' vs 'down' vs 'flat'; growth from a zero baseline is\n * classified `up` with a `null` percent (see {@link classifyTrend}).\n *\n * Day boundaries are computed in `timeZone` (an IANA identifier such as the\n * property's `AnalyticsProperty.timeZone`, which defaults to\n * `America/Los_Angeles`) so an event near local midnight buckets into the\n * correct calendar day. Defaults to `'UTC'` when omitted.\n *\n * @param propertyId - Property ID\n * @param now - Optional current date (for testing)\n * @param timeZone - IANA time zone for day boundaries (default `'UTC'`)\n * @returns Stats with trend\n */\n async getPropertyStatsWithTrend(\n propertyId: string,\n now?: Date,\n timeZone: string = 'UTC',\n ): Promise<PropertyStatsWithTrend> {\n const currentTime = now || new Date();\n const todayStart = this.startOfDayInZone(currentTime, timeZone);\n const yesterdayStart = this.startOfYesterdayInZone(todayStart, timeZone);\n\n // Single query for both days to avoid concurrent DuckDB prepared statements\n const allPageviewEvents = await this.list({\n where: {\n propertyId,\n eventName: 'page_view',\n 'eventTimestamp >=': yesterdayStart.toISOString(),\n 'eventTimestamp <=': currentTime.toISOString(),\n },\n });\n\n const todayPageviewEvents = allPageviewEvents.filter(\n (e) => new Date(e.eventTimestamp) >= todayStart,\n );\n const yesterdayPageviewEvents = allPageviewEvents.filter(\n (e) => new Date(e.eventTimestamp) < todayStart,\n );\n\n // Count unique clients (users)\n const todayClients = new Set(todayPageviewEvents.map((e) => e.clientId));\n const yesterdayClients = new Set(\n yesterdayPageviewEvents.map((e) => e.clientId),\n );\n\n const todayPageviews = todayPageviewEvents.length;\n const yesterdayPageviews = yesterdayPageviewEvents.length;\n\n const { trend, trendPercent } = this.classifyTrend(\n todayPageviews,\n yesterdayPageviews,\n );\n\n return {\n todayPageviews,\n todayUsers: todayClients.size,\n yesterdayPageviews,\n yesterdayUsers: yesterdayClients.size,\n trend,\n trendPercent,\n };\n }\n\n /**\n * Get day-over-day stats for multiple properties in batch.\n *\n * Day boundaries are computed in `timeZone` (default `'UTC'`); see\n * {@link getPropertyStatsWithTrend}. A single zone applies to the whole\n * batch, so callers mixing properties with different `timeZone` values\n * should batch per zone (or fall back to per-property calls).\n *\n * @param propertyIds - Array of property IDs\n * @param now - Optional current date (for testing)\n * @param timeZone - IANA time zone for day boundaries (default `'UTC'`)\n * @returns Map of propertyId to stats\n */\n async getBatchPropertyStats(\n propertyIds: string[],\n now?: Date,\n timeZone: string = 'UTC',\n ): Promise<Map<string, PropertyStatsWithTrend>> {\n const results = new Map<string, PropertyStatsWithTrend>();\n\n // Fetch all date-ranged events once to avoid N+1 queries\n const currentTime = now || new Date();\n const todayStart = this.startOfDayInZone(currentTime, timeZone);\n const yesterdayStart = this.startOfYesterdayInZone(todayStart, timeZone);\n\n // Single query for both days to avoid concurrent DuckDB prepared statements\n const allEvents = await this.list({\n where: {\n eventName: 'page_view',\n 'eventTimestamp >=': yesterdayStart.toISOString(),\n 'eventTimestamp <=': currentTime.toISOString(),\n },\n });\n\n // Pre-group events by propertyId and day in a single pass\n const todayByProperty = new Map<string, AnalyticsEvent[]>();\n const yesterdayByProperty = new Map<string, AnalyticsEvent[]>();\n for (const e of allEvents) {\n const isToday = new Date(e.eventTimestamp) >= todayStart;\n const map = isToday ? todayByProperty : yesterdayByProperty;\n const list = map.get(e.propertyId);\n if (list) list.push(e);\n else map.set(e.propertyId, [e]);\n }\n\n for (const propertyId of propertyIds) {\n const todayPageviewEvents = todayByProperty.get(propertyId) ?? [];\n const yesterdayPageviewEvents = yesterdayByProperty.get(propertyId) ?? [];\n\n const todayClients = new Set(todayPageviewEvents.map((e) => e.clientId));\n const yesterdayClients = new Set(\n yesterdayPageviewEvents.map((e) => e.clientId),\n );\n\n const todayPageviews = todayPageviewEvents.length;\n const yesterdayPageviews = yesterdayPageviewEvents.length;\n\n const { trend, trendPercent } = this.classifyTrend(\n todayPageviews,\n yesterdayPageviews,\n );\n\n results.set(propertyId, {\n todayPageviews,\n todayUsers: todayClients.size,\n yesterdayPageviews,\n yesterdayUsers: yesterdayClients.size,\n trend,\n trendPercent,\n });\n }\n\n return results;\n }\n}\n","/**\n * AnalyticsProperty model - Represents an analytics property/site\n * @packageDocumentation\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n promptMessageOptions,\n smrtAnalyticsAnalyzePerformancePrompt,\n} from '../prompts.js';\nimport { AnalyticsPropertyStatus, AnalyticsProvider } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsProperty}.\n */\nexport interface AnalyticsPropertyOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n name?: string;\n displayName?: string;\n provider?: AnalyticsProvider;\n externalId?: string;\n measurementId?: string;\n apiSecret?: string;\n siteDomain?: string;\n timeZone?: string;\n currencyCode?: string;\n industryCategory?: string;\n serviceLevel?: string;\n status?: AnalyticsPropertyStatus;\n lastSyncAt?: Date | null;\n providerMetadata?: string;\n}\n\n/**\n * AnalyticsProperty represents an analytics property (GA4 property,\n * Plausible site, or Matomo site).\n *\n * @example\n * ```typescript\n * const property = await properties.create({\n * name: 'My Website',\n * displayName: 'My Website Analytics',\n * provider: AnalyticsProvider.GA4,\n * externalId: 'properties/123456789',\n * measurementId: 'G-XXXXXXXXXX'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'sync', 'runReport'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsProperty extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Without tenant scoping the generated `list`/`get` API and any raw query\n * return every tenant's properties — including the `@field({ sensitive })`\n * `apiSecret` / `providerMetadata` credentials. `@TenantScoped` registers\n * this class with the tenant interceptor so reads are auto-filtered and\n * writes are bound to the active tenant context.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Internal name/identifier\n */\n name: string = '';\n\n /**\n * Human-readable display name\n */\n @field({ description: 'The name people see for this website or app.' })\n displayName: string = '';\n\n /**\n * Analytics provider (ga4, plausible, matomo)\n */\n @field({\n description:\n 'Which analytics service tracks it: Google Analytics 4, Plausible or Matomo.',\n })\n provider: AnalyticsProvider = AnalyticsProvider.GA4;\n\n /**\n * External ID from the provider (e.g., \"properties/123456789\" for GA4,\n * idSite for Matomo)\n */\n @field({\n description:\n 'The id the analytics service gives the property, such as properties/123456789 or a Matomo site id.',\n })\n externalId: string = '';\n\n /**\n * Measurement ID for GA4 (G-XXXXXXXXXX)\n */\n @field({\n description: 'The tracking id for a web stream, such as G-XXXXXXXXXX.',\n })\n measurementId: string = '';\n\n /**\n * Provider API secret/token (GA4 API secret, Matomo token_auth)\n *\n * Sensitive (#1540): excluded from generated API/MCP responses and rejected\n * as a `where` filter key so it can't be probed.\n */\n @field({\n sensitive: true,\n description:\n 'The secret key your analytics service issues for this property. It is kept out of responses and tool results, but it is not encrypted in the database.',\n })\n apiSecret: string = '';\n\n /**\n * Site domain for Plausible/Matomo\n */\n @field({ description: 'The website address, used by Plausible and Matomo.' })\n siteDomain: string = '';\n\n /**\n * Property timezone\n */\n @field({ description: 'The time zone the figures are reported in.' })\n timeZone: string = 'America/Los_Angeles';\n\n /**\n * Currency code (e.g., 'USD', 'EUR')\n */\n @field({\n description: 'The currency used for revenue figures, such as USD.',\n })\n currencyCode: string = 'USD';\n\n /**\n * Industry category\n */\n industryCategory: string = '';\n\n /**\n * Service level (STANDARD, PREMIUM)\n */\n serviceLevel: string = 'STANDARD';\n\n /**\n * Property status\n */\n @field({\n description: 'Whether tracking is active, inactive or still being set up.',\n })\n status: AnalyticsPropertyStatus = AnalyticsPropertyStatus.ACTIVE;\n\n /**\n * Last sync timestamp with provider\n */\n @field({\n description:\n 'When the property was last synced with the analytics service.',\n })\n lastSyncAt: Date | null = null;\n\n /**\n * Metadata from provider (JSON)\n *\n * Sensitive (#1540): may carry provider credentials/tokens, so it is excluded\n * from generated API/MCP responses and rejected as a `where` filter key.\n */\n @field({\n sensitive: true,\n description:\n 'Extra connection details from your analytics service. They are kept out of responses and tool results, but they are not encrypted in the database.',\n })\n providerMetadata: string = '{}';\n\n constructor(options: AnalyticsPropertyOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.displayName !== undefined)\n this.displayName = options.displayName;\n if (options.provider !== undefined) this.provider = options.provider;\n if (options.externalId !== undefined) this.externalId = options.externalId;\n if (options.measurementId !== undefined)\n this.measurementId = options.measurementId;\n if (options.apiSecret !== undefined) this.apiSecret = options.apiSecret;\n if (options.siteDomain !== undefined) this.siteDomain = options.siteDomain;\n if (options.timeZone !== undefined) this.timeZone = options.timeZone;\n if (options.currencyCode !== undefined)\n this.currencyCode = options.currencyCode;\n if (options.industryCategory !== undefined)\n this.industryCategory = options.industryCategory;\n if (options.serviceLevel !== undefined)\n this.serviceLevel = options.serviceLevel;\n if (options.status !== undefined) this.status = options.status;\n if (options.lastSyncAt !== undefined) this.lastSyncAt = options.lastSyncAt;\n if (options.providerMetadata !== undefined)\n this.providerMetadata = options.providerMetadata;\n }\n\n /**\n * Check if this is a GA4 property\n */\n isGA4(): boolean {\n return this.provider === AnalyticsProvider.GA4;\n }\n\n /**\n * Check if this is a Plausible site\n */\n isPlausible(): boolean {\n return this.provider === AnalyticsProvider.PLAUSIBLE;\n }\n\n /**\n * Check if this is a Matomo site\n */\n isMatomo(): boolean {\n return this.provider === AnalyticsProvider.MATOMO;\n }\n\n /**\n * Get parsed provider metadata\n */\n getProviderMetadata(): Record<string, unknown> {\n try {\n return JSON.parse(this.providerMetadata);\n } catch {\n return {};\n }\n }\n\n /**\n * Set provider metadata\n */\n setProviderMetadata(metadata: Record<string, unknown>): void {\n this.providerMetadata = JSON.stringify(metadata);\n }\n\n /**\n * Mark as synced with provider\n */\n markSynced(): void {\n this.lastSyncAt = new Date();\n }\n\n /**\n * AI-powered: Analyze property performance.\n *\n * Uses the `smrtAnalytics.property.analyzePerformance` prompt registered\n * via `@happyvertical/smrt-prompts`, allowing tenant- or instance-level\n * overrides of the template, model, and parameters at runtime.\n *\n * Only non-PII fields (display name, provider label, requested period)\n * are sent to the AI provider. Internal identifiers (`id`, `externalId`,\n * `measurementId`, `apiSecret`, `providerMetadata`) are intentionally\n * excluded — see `../prompts.ts` for the full exclusion rationale.\n */\n async analyzePerformance(options: { period?: string } = {}): Promise<{\n action: string;\n period: string;\n analysis: string;\n }> {\n const period = options.period || '30 days';\n\n // Resolve `db` from either the canonical `db` option or its `persistence`\n // alias so stored prompt overrides are honored on first call before\n // `getAiClient()` triggers full initialization. SmrtObject already types\n // both options on `SmrtClassOptions` so no `any` cast is needed — that\n // keeps a misspelt option name surfacing as a TypeScript error rather\n // than silently falling through.\n const db = this.options.db ?? this.options.persistence;\n\n // This model is `@TenantScoped` and declares a `tenantId` field, but we\n // still deliberately OMIT `tenantId` from the resolvePrompt options so the\n // resolver falls back to the AsyncLocalStorage tenancy context via\n // `getTenantId()`. Passing `this.tenantId` (which may be null for\n // tenant-agnostic rows) explicitly would short-circuit that fallback and\n // silently ignore tenant-specific prompt overrides active in the\n // surrounding `withTenant(...)` block.\n const resolvedPrompt = await resolvePrompt(\n smrtAnalyticsAnalyzePerformancePrompt.key,\n {\n db,\n variables: {\n period,\n propertyDisplayName: this.displayName || '',\n propertyProvider: this.provider || '',\n },\n },\n );\n\n const ai = await this.getAiClient();\n const analysis = await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n );\n\n return {\n action: 'analyzePerformance',\n period,\n analysis: analysis.trim(),\n };\n }\n\n /**\n * AI-powered: Check if property is performing well\n */\n async isPerformingWell(): Promise<boolean> {\n return await this.is(\n `\n Based on the property configuration and metadata:\n - Property: ${this.displayName}\n - Provider: ${this.provider}\n - Status: ${this.status}\n - Last sync: ${this.lastSyncAt}\n\n Is this property properly configured and likely performing well?\n `,\n // Property fields hand-rolled above; skip is()'s object-data injection.\n { includeData: false },\n );\n }\n}\n\nexport default AnalyticsProperty;\n","/**\n * AnalyticsPropertyCollection - Collection manager for AnalyticsProperty objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsProperty } from '../models/AnalyticsProperty.js';\nimport { AnalyticsPropertyStatus, AnalyticsProvider } from '../types/index.js';\n\nexport class AnalyticsPropertyCollection extends SmrtCollection<AnalyticsProperty> {\n static readonly _itemClass = AnalyticsProperty;\n\n /**\n * Find property by external ID\n *\n * @param externalId - External ID from provider\n * @returns Matching property or null\n */\n async findByExternalId(\n externalId: string,\n ): Promise<AnalyticsProperty | null> {\n const results = await this.list({\n where: { externalId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find property by measurement ID (GA4)\n *\n * @param measurementId - GA4 measurement ID (G-XXXXXXXXXX)\n * @returns Matching property or null\n */\n async findByMeasurementId(\n measurementId: string,\n ): Promise<AnalyticsProperty | null> {\n const results = await this.list({\n where: { measurementId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find property by site domain and optional provider.\n *\n * @param siteDomain - Provider site domain\n * @param provider - Optional provider discriminator for migration/coexistence\n * @returns Matching property or null\n */\n async findBySiteDomain(\n siteDomain: string,\n provider?: AnalyticsProvider,\n ): Promise<AnalyticsProperty | null> {\n const results = await this.list({\n where: provider ? { siteDomain, provider } : { siteDomain },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find properties by provider\n *\n * @param provider - Analytics provider\n * @returns Array of matching properties\n */\n async findByProvider(\n provider: AnalyticsProvider,\n ): Promise<AnalyticsProperty[]> {\n return await this.list({\n where: { provider },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all GA4 properties\n */\n async findGA4Properties(): Promise<AnalyticsProperty[]> {\n return await this.findByProvider(AnalyticsProvider.GA4);\n }\n\n /**\n * Find all Plausible sites\n */\n async findPlausibleSites(): Promise<AnalyticsProperty[]> {\n return await this.findByProvider(AnalyticsProvider.PLAUSIBLE);\n }\n\n /**\n * Find all Matomo sites\n */\n async findMatomoSites(): Promise<AnalyticsProperty[]> {\n return await this.findByProvider(AnalyticsProvider.MATOMO);\n }\n\n /**\n * Find properties by status\n *\n * @param status - Property status\n * @returns Array of matching properties\n */\n async findByStatus(\n status: AnalyticsPropertyStatus,\n ): Promise<AnalyticsProperty[]> {\n return await this.list({\n where: { status },\n orderBy: 'displayName ASC',\n });\n }\n\n /**\n * Find all active properties\n */\n async findActive(): Promise<AnalyticsProperty[]> {\n return await this.findByStatus(AnalyticsPropertyStatus.ACTIVE);\n }\n\n /**\n * Find properties that need syncing (not synced in last N hours)\n *\n * @param hoursAgo - Hours since last sync\n * @returns Array of properties needing sync\n */\n async findNeedingSync(hoursAgo: number = 24): Promise<AnalyticsProperty[]> {\n const cutoff = new Date();\n cutoff.setTime(cutoff.getTime() - hoursAgo * 60 * 60 * 1000);\n\n // Get properties where lastSyncAt is null or older than cutoff\n const all = await this.findActive();\n return all.filter((p) => !p.lastSyncAt || p.lastSyncAt < cutoff);\n }\n}\n","/**\n * AnalyticsReport model - Saved report configurations and results\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n promptMessageOptions,\n smrtAnalyticsAnalyzeResultsPrompt,\n smrtAnalyticsHasPositiveTrendsPrompt,\n} from '../prompts.js';\nimport { ReportFrequency, ReportStatus } from '../types/index.js';\n\n/**\n * Options for constructing an {@link AnalyticsReport}.\n *\n * `metrics` is omitted from the {@link SmrtObjectOptions} base before being\n * re-declared: the framework option carries an observability `MetricsConfig`,\n * whereas this report field is a JSON-encoded list of analytics metrics.\n */\nexport interface AnalyticsReportOptions\n extends Omit<SmrtObjectOptions, 'metrics'> {\n tenantId?: string | null;\n propertyId?: string;\n name?: string;\n description?: string;\n dimensions?: string;\n metrics?: string;\n dateRangeStart?: string;\n dateRangeEnd?: string;\n dimensionFilter?: string;\n metricFilter?: string;\n orderBy?: string;\n maxResults?: number;\n status?: ReportStatus;\n frequency?: ReportFrequency;\n lastRunAt?: Date | null;\n nextRunAt?: Date | null;\n resultData?: string;\n rowCount?: number;\n lastError?: string;\n}\n\n/**\n * AnalyticsReport represents a saved report configuration with optional scheduling.\n *\n * @example\n * ```typescript\n * const report = await reports.create({\n * propertyId: property.id,\n * name: 'Weekly Traffic Report',\n * dimensions: JSON.stringify([{ name: 'country' }, { name: 'deviceCategory' }]),\n * metrics: JSON.stringify([{ name: 'activeUsers' }, { name: 'sessions' }]),\n * frequency: ReportFrequency.WEEKLY\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class AnalyticsReport extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-analytics\";\n /**\n * Tenant ID for multi-tenancy isolation (#1410).\n *\n * Reports persist `resultData` rows that may contain tenant-private metrics\n * and PII-bearing dimensions. Without tenant scoping the generated\n * `list`/`get` API returns every tenant's cached report data, and the\n * AI-powered `analyze`/`run` operations could run over another tenant's\n * rows. `@TenantScoped` auto-filters reads and binds writes to the tenant.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent property ID (references AnalyticsProperty)\n */\n @foreignKey('AnalyticsProperty', {\n description: 'The website or app this report is about.',\n })\n propertyId: string = '';\n\n /**\n * Report name\n */\n @field({\n description: 'A short name for the report, such as Weekly traffic.',\n })\n name: string = '';\n\n /**\n * Report description\n */\n @field({ description: 'What the report is for.' })\n description: string = '';\n\n /**\n * Dimensions to group by (JSON array)\n */\n @field({\n description:\n 'What to break the figures down by, such as country or device, as a list.',\n })\n dimensions: string = '[]';\n\n /**\n * Metrics to retrieve (JSON array)\n */\n @field({\n description: 'What to count, such as active users or sessions, as a list.',\n })\n metrics: string = '[]';\n\n /**\n * Date range start (relative or absolute)\n */\n @field({\n description:\n 'Where the period begins: a date, or a relative value such as 7daysAgo.',\n })\n dateRangeStart: string = '7daysAgo';\n\n /**\n * Date range end (relative or absolute)\n */\n @field({\n description:\n 'Where the period ends: a date, or a relative value such as today.',\n })\n dateRangeEnd: string = 'today';\n\n /**\n * Dimension filter expression (JSON)\n */\n dimensionFilter: string = '';\n\n /**\n * Metric filter expression (JSON)\n */\n metricFilter: string = '';\n\n /**\n * Sort order (JSON array)\n */\n orderBy: string = '[]';\n\n /**\n * Maximum results to return\n */\n maxResults: number = 0;\n\n /**\n * Report status\n */\n @field({\n description:\n 'Where the report stands: draft, scheduled, running, completed or failed.',\n })\n status: ReportStatus = ReportStatus.DRAFT;\n\n /**\n * Scheduling frequency\n */\n @field({\n description:\n 'How often the report repeats: once, daily, weekly or monthly.',\n })\n frequency: ReportFrequency = ReportFrequency.ONCE;\n\n /**\n * Last run timestamp\n */\n @field({ description: 'When the report last ran.' })\n lastRunAt: Date | null = null;\n\n /**\n * Next scheduled run\n */\n @field({ description: 'When the report is next due to run.' })\n nextRunAt: Date | null = null;\n\n /**\n * Cached result data (JSON)\n */\n resultData: string = '';\n\n /**\n * Row count from last run\n */\n @field({ description: 'How many rows the last run returned.' })\n rowCount: number = 0;\n\n /**\n * Error message from last failed run\n */\n @field({ description: 'Why the last run failed. Empty when it worked.' })\n lastError: string = '';\n\n constructor(options: AnalyticsReportOptions = {}) {\n // `AnalyticsReportOptions` re-types `metrics` as the report's JSON string,\n // which collides with the framework's `metrics?: MetricsConfig` observability\n // option on `SmrtObjectOptions`. The base never reads a string `metrics` as\n // config, so the value is forwarded unchanged; the assertion only reconciles\n // the two unrelated `metrics` types at the `super()` boundary.\n super(options as SmrtObjectOptions);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.propertyId !== undefined) this.propertyId = options.propertyId;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.dimensions !== undefined) this.dimensions = options.dimensions;\n if (options.metrics !== undefined) this.metrics = options.metrics;\n if (options.dateRangeStart !== undefined)\n this.dateRangeStart = options.dateRangeStart;\n if (options.dateRangeEnd !== undefined)\n this.dateRangeEnd = options.dateRangeEnd;\n if (options.dimensionFilter !== undefined)\n this.dimensionFilter = options.dimensionFilter;\n if (options.metricFilter !== undefined)\n this.metricFilter = options.metricFilter;\n if (options.orderBy !== undefined) this.orderBy = options.orderBy;\n if (options.maxResults !== undefined) this.maxResults = options.maxResults;\n if (options.status !== undefined) this.status = options.status;\n if (options.frequency !== undefined) this.frequency = options.frequency;\n if (options.lastRunAt !== undefined) this.lastRunAt = options.lastRunAt;\n if (options.nextRunAt !== undefined) this.nextRunAt = options.nextRunAt;\n if (options.resultData !== undefined) this.resultData = options.resultData;\n if (options.rowCount !== undefined) this.rowCount = options.rowCount;\n if (options.lastError !== undefined) this.lastError = options.lastError;\n }\n\n /**\n * Get parsed dimensions\n */\n getDimensions(): Array<{ name: string }> {\n try {\n return JSON.parse(this.dimensions);\n } catch {\n return [];\n }\n }\n\n /**\n * Set dimensions\n */\n setDimensions(dimensions: Array<{ name: string }>): void {\n this.dimensions = JSON.stringify(dimensions);\n }\n\n /**\n * Get parsed metrics\n */\n getMetrics(): Array<{ name: string }> {\n try {\n return JSON.parse(this.metrics);\n } catch {\n return [];\n }\n }\n\n /**\n * Set metrics\n */\n setMetrics(metrics: Array<{ name: string }>): void {\n this.metrics = JSON.stringify(metrics);\n }\n\n /**\n * Get parsed result data\n */\n getResultData(): Record<string, unknown> | null {\n if (!this.resultData) return null;\n try {\n return JSON.parse(this.resultData);\n } catch {\n return null;\n }\n }\n\n /**\n * Set result data\n */\n setResultData(data: Record<string, unknown>): void {\n this.resultData = JSON.stringify(data);\n }\n\n /**\n * Mark report as running\n */\n markRunning(): void {\n this.status = ReportStatus.RUNNING;\n this.lastError = '';\n }\n\n /**\n * Mark report as completed with results\n */\n markCompleted(rowCount: number): void {\n this.status = ReportStatus.COMPLETED;\n this.lastRunAt = new Date();\n this.rowCount = rowCount;\n this.lastError = '';\n this.calculateNextRun();\n }\n\n /**\n * Mark report as failed\n */\n markFailed(error: string): void {\n this.status = ReportStatus.FAILED;\n this.lastRunAt = new Date();\n this.lastError = error;\n this.calculateNextRun();\n }\n\n /**\n * Calculate next scheduled run based on frequency\n */\n calculateNextRun(): void {\n if (this.frequency === ReportFrequency.ONCE) {\n this.nextRunAt = null;\n return;\n }\n\n const now = new Date();\n const next = new Date(now);\n\n switch (this.frequency) {\n case ReportFrequency.DAILY:\n next.setDate(next.getDate() + 1);\n break;\n case ReportFrequency.WEEKLY:\n next.setDate(next.getDate() + 7);\n break;\n case ReportFrequency.MONTHLY:\n next.setMonth(next.getMonth() + 1);\n break;\n }\n\n this.nextRunAt = next;\n }\n\n /**\n * Check if report is due to run\n */\n isDue(): boolean {\n if (this.frequency === ReportFrequency.ONCE) {\n return this.status === ReportStatus.SCHEDULED && !this.lastRunAt;\n }\n if (!this.nextRunAt) return false;\n return new Date() >= this.nextRunAt;\n }\n\n /**\n * AI-powered: Analyze report results.\n *\n * Uses the `smrtAnalytics.report.analyzeResults` prompt registered via\n * `@happyvertical/smrt-prompts`, allowing tenant- or instance-level\n * overrides of the template, model, and parameters at runtime.\n *\n * Internal identifiers (`id`, `propertyId`, `tenantId`, `lastError`, raw\n * `dimensionFilter` / `metricFilter` JSON) are excluded from the prompt\n * variables — see `../prompts.ts` for the exclusion rationale.\n *\n * **`resultData` is FORWARDED VERBATIM.** The persisted result rows are\n * JSON-stringified into the `reportData` variable; this package cannot\n * strip PII because the row schema is determined by which dimensions /\n * metrics the caller asked the analytics provider to return. If the\n * persisted rows contain `userPseudoId`, `clientId`, IP-derived\n * geolocation, or any other identifier, those fields WILL reach the AI\n * provider. Callers are responsible for excluding PII-bearing dimensions\n * before persisting, applying a column allowlist at the call site, or\n * overriding the prompt template via `PromptOverride`. The forwarding is\n * pinned by a regression test in\n * `__tests__/analytics-report-prompt.test.ts`.\n *\n * The previous implementation issued a second freeform `this.do()` call\n * to re-summarize \"top 3 insights\"; that behaviour is now folded into\n * the single registered template (which already asks for findings,\n * trends, and recommendations) — `insights` mirrors `analysis` so the\n * return shape is preserved without a redundant AI round-trip.\n */\n async analyzeResults(_options: Record<string, unknown> = {}): Promise<{\n action: string;\n analysis: string;\n insights: string;\n }> {\n const resultData = this.getResultData();\n\n // Resolve `db` from either the canonical `db` option or its `persistence`\n // alias so stored prompt overrides are honored on first call before\n // `getAiClient()` triggers full initialization. SmrtObject already types\n // both options on `SmrtClassOptions` so no `any` cast is needed — that\n // keeps a misspelt option name surfacing as a TypeScript error rather\n // than silently falling through.\n const db = this.options.db ?? this.options.persistence;\n\n // This model is `@TenantScoped` and declares a `tenantId` field, but we\n // still deliberately OMIT `tenantId` from the resolvePrompt options so the\n // resolver falls back to the AsyncLocalStorage tenancy context via\n // `getTenantId()`. Passing `this.tenantId` (which may be null for\n // tenant-agnostic rows) explicitly would short-circuit that fallback and\n // silently ignore tenant-specific prompt overrides active in the\n // surrounding `withTenant(...)` block.\n const resolvedPrompt = await resolvePrompt(\n smrtAnalyticsAnalyzeResultsPrompt.key,\n {\n db,\n variables: {\n reportName: this.name || '',\n reportDimensions: this.dimensions || '[]',\n reportMetrics: this.metrics || '[]',\n dateRangeStart: this.dateRangeStart || '',\n dateRangeEnd: this.dateRangeEnd || '',\n rowCount: String(this.rowCount),\n reportData: JSON.stringify(resultData, null, 2),\n },\n },\n );\n\n const ai = await this.getAiClient();\n const analysis = (\n await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n )\n ).trim();\n\n return {\n action: 'analyzeResults',\n analysis,\n insights: analysis,\n };\n }\n\n /**\n * AI-powered: Check if results show positive trends.\n *\n * Uses the `smrtAnalytics.report.hasPositiveTrends` prompt registered\n * via `@happyvertical/smrt-prompts`. Only the metric labels and the\n * aggregate `resultData` JSON are sent to the AI provider — though as\n * with `analyzeResults`, `resultData` is forwarded verbatim and may\n * carry PII the caller persisted; see `analyzeResults` docstring and\n * `../prompts.ts`.\n *\n * When a typed-decision provider is configured and this object has no\n * registered tools, the resolved prompt is sent as one bounded predicate\n * request. The report's selected metrics and persisted aggregate data are\n * the complete decision state; object fields and internal filters are never\n * added. The decision uses the configured threshold (default `0.5`). A\n * configured uncertainty band falls back to the same resolved generative\n * prompt, matching `SmrtObject.evaluate()`; provider, capability, and\n * malformed-response errors propagate.\n *\n * Without a decision provider, or where tools require the generative route,\n * Boolean coercion uses `/^\\s*(yes|true)\\b/i` against the trimmed response.\n * The registered prompt template explicitly instructs the model to begin its\n * answer with the literal word \"yes\" or \"no\" so this legacy route remains\n * compatible; tenant overrides MUST preserve that leading-word convention.\n */\n async hasPositiveTrends(): Promise<boolean> {\n const resultData = this.getResultData();\n\n // See `analyzeResults` above for the typed-options + tenancy-fallback rationale.\n const db = this.options.db ?? this.options.persistence;\n\n const resolvedPrompt = await resolvePrompt(\n smrtAnalyticsHasPositiveTrendsPrompt.key,\n {\n db,\n variables: {\n reportMetrics: this.metrics || '[]',\n reportData: JSON.stringify(resultData),\n },\n },\n );\n\n // Typed decisions cannot call tools. Check this before resolving the\n // optional client so a tool-enabled report stays on its existing route.\n if (this.getAvailableTools().length === 0) {\n const config = this.getDecisionConfig();\n const threshold = config?.threshold ?? 0.5;\n if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {\n throw new Error(\n 'Decision threshold must be a finite number in [0, 1].',\n );\n }\n const uncertaintyBand = config?.uncertaintyFallback?.band;\n if (\n uncertaintyBand !== undefined &&\n (!Number.isFinite(uncertaintyBand) ||\n uncertaintyBand < 0 ||\n uncertaintyBand > 1)\n ) {\n throw new Error(\n 'Decision uncertainty fallback band must be a finite number in [0, 1].',\n );\n }\n\n const decision = await this.attemptDecision({\n state: {\n reportMetrics: this.metrics || '[]',\n reportData: JSON.stringify(resultData),\n },\n questions: {\n result: {\n type: 'predicate',\n instructions: `${resolvedPrompt.text}\\n\\nFor this predicate, only treat higher values as favorable for user growth, engagement, and conversions. Treat higher bounce or error rates as adverse. Do not infer a direction for an unknown metric. A date-range label alone does not establish comparable periods; if the supplied data is incomplete, lacks a comparable period, or is otherwise insufficient or ambiguous, answer false.`,\n },\n },\n });\n\n if (decision) {\n const answer = decision.answers.result;\n if (answer?.type !== 'predicate') {\n throw new Error('Decision provider returned no predicate result.');\n }\n\n const probability = answer.probability;\n if (uncertaintyBand !== undefined) {\n const outsideUncertaintyBand =\n probability < Math.max(0, threshold - uncertaintyBand) ||\n probability > Math.min(1, threshold + uncertaintyBand);\n if (outsideUncertaintyBand) {\n return probability >= threshold;\n }\n } else {\n return probability >= threshold;\n }\n }\n }\n\n const ai = await this.getAiClient();\n const response = (\n await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n )\n ).trim();\n\n return /^\\s*(yes|true)\\b/i.test(response);\n }\n}\n\nexport default AnalyticsReport;\n","/**\n * AnalyticsReportCollection - Collection manager for AnalyticsReport objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AnalyticsReport } from '../models/AnalyticsReport.js';\nimport { ReportFrequency, ReportStatus } from '../types/index.js';\n\nexport class AnalyticsReportCollection extends SmrtCollection<AnalyticsReport> {\n static readonly _itemClass = AnalyticsReport;\n\n /**\n * Find reports by property\n *\n * @param propertyId - Parent property ID\n * @returns Array of reports\n */\n async findByProperty(propertyId: string): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { propertyId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find reports by status\n *\n * @param status - Report status\n * @returns Array of matching reports\n */\n async findByStatus(status: ReportStatus): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { status },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all draft reports\n */\n async findDrafts(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.DRAFT);\n }\n\n /**\n * Find all scheduled reports\n */\n async findScheduled(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.SCHEDULED);\n }\n\n /**\n * Find all completed reports\n */\n async findCompleted(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.COMPLETED);\n }\n\n /**\n * Find all failed reports\n */\n async findFailed(): Promise<AnalyticsReport[]> {\n return await this.findByStatus(ReportStatus.FAILED);\n }\n\n /**\n * Find reports by frequency\n *\n * @param frequency - Report frequency\n * @returns Array of matching reports\n */\n async findByFrequency(\n frequency: ReportFrequency,\n ): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { frequency },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all recurring reports (not one-time)\n */\n async findRecurring(): Promise<AnalyticsReport[]> {\n const all = await this.list({ orderBy: 'name ASC' });\n return all.filter((r) => r.frequency !== ReportFrequency.ONCE);\n }\n\n /**\n * Find reports due to run\n *\n * @returns Array of reports that are due\n */\n async findDue(): Promise<AnalyticsReport[]> {\n const all = await this.list({\n where: { status: ReportStatus.SCHEDULED },\n });\n return all.filter((r) => r.isDue());\n }\n\n /**\n * Find reports for a property by status\n *\n * @param propertyId - Parent property ID\n * @param status - Report status\n * @returns Array of matching reports\n */\n async findByPropertyAndStatus(\n propertyId: string,\n status: ReportStatus,\n ): Promise<AnalyticsReport[]> {\n return await this.list({\n where: { propertyId, status },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find recently run reports\n *\n * @param hoursAgo - Hours since last run\n * @returns Array of recently run reports\n */\n async findRecentlyRun(hoursAgo: number = 24): Promise<AnalyticsReport[]> {\n const cutoff = new Date();\n cutoff.setTime(cutoff.getTime() - hoursAgo * 60 * 60 * 1000);\n\n const all = await this.list({ orderBy: 'lastRunAt DESC' });\n return all.filter((r) => r.lastRunAt && r.lastRunAt >= cutoff);\n }\n}\n","/**\n * Declared recipes for smrt-analytics (#3719): user-facing units of\n * functionality an app or agent can pick instead of the whole package.\n * The scanner reads these statics into the `recipes` array of `manifest.json`\n * and `smrt-knowledge.json`; nothing here runs at that point.\n *\n * `analytics.reports` covers properties and saved reports. Event tracking\n * (`AnalyticsEvent`) and data streams are separate concerns and are left out.\n *\n * @packageDocumentation\n */\n\nimport { SmrtRecipe } from '@happyvertical/smrt-core';\nimport { AnalyticsProperty } from './models/AnalyticsProperty.js';\nimport { AnalyticsReport } from './models/AnalyticsReport.js';\n\n/** Analytics reports: saved traffic reports and an AI read on the results. */\nexport class AnalyticsReportsRecipe extends SmrtRecipe {\n static id = 'analytics.reports';\n static help = './reports.recipe.md';\n static label = 'Analytics';\n static summary =\n 'Save traffic reports for your websites and apps and get a plain-language read on the results.';\n static synonyms = [\n 'analytics',\n 'traffic',\n 'pageviews',\n 'visitors',\n 'web stats',\n 'google analytics',\n 'plausible',\n 'matomo',\n ];\n static section = {\n id: 'analytics',\n label: 'Analytics',\n icon: 'layers',\n description: 'How your websites and apps are doing.',\n };\n static models = [AnalyticsProperty, AnalyticsReport];\n // Stored reports and their AI summaries need the app database and a server\n // held AI key; the provider credentials live on the property row. Only the\n // view component below renders in the browser, from data the server hands it.\n static runtime = 'server' as const;\n // `AnalyticsSummary` renders an empty state without data. `PropertyInfo` and\n // `EventsTable` take required row props and would fail with none, so they are\n // not routes; a host places them inside its own pages.\n static surfaces = [\n {\n kind: 'route',\n path: '/analytics/summary',\n export: '@happyvertical/smrt-analytics/svelte#AnalyticsSummary',\n label: 'Traffic summary',\n },\n ] as const;\n // Icons are shell icon names (`SHELL_ICON_PATHS` in smrt-svelte), which have\n // no chart or globe glyph; the section and entries use the closest ones.\n static nav = [\n {\n label: 'Analytics properties',\n model: AnalyticsProperty,\n icon: 'home',\n description: 'The websites and apps you track, and who tracks them.',\n },\n {\n label: 'Analytics reports',\n model: AnalyticsReport,\n icon: 'fileText',\n description: 'Saved reports, how often they run and what they found.',\n },\n ];\n // Literal values (`as const` keeps `visibility` a literal type): the scanner\n // reads these statically. Internal naming and raw query JSON stay out of the\n // way; stored result rows are read through the report, not edited by hand.\n static options = {\n AnalyticsProperty: {\n fields: {\n name: { visibility: 'hidden' },\n industryCategory: { visibility: 'advanced' },\n serviceLevel: { visibility: 'advanced' },\n lastSyncAt: { locked: true },\n },\n },\n AnalyticsReport: {\n fields: {\n dimensionFilter: { visibility: 'advanced' },\n metricFilter: { visibility: 'advanced' },\n orderBy: { visibility: 'advanced' },\n maxResults: { visibility: 'advanced' },\n resultData: { visibility: 'hidden', locked: true },\n rowCount: { locked: true },\n lastRunAt: { locked: true },\n nextRunAt: { locked: true },\n lastError: { locked: true },\n },\n },\n } as const;\n}\n"],"mappings":"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"}