Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions src/platform/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,40 @@
*/
import { isCloud } from '@/platform/distribution/types'

import { CloudAnalyticsProvider } from './providers/cloud/CloudAnalyticsProvider'
import { MixpanelTelemetryProvider } from './providers/cloud/MixpanelTelemetryProvider'
import type { TelemetryProvider } from './types'
import { TelemetryService } from './services/TelemetryService'

// Singleton instance
let _telemetryProvider: TelemetryProvider | null = null
let _telemetryService: TelemetryService | null = null

/**
* Telemetry factory - conditionally creates provider based on distribution
* Telemetry service factory - conditionally creates service with providers based on distribution
* Returns singleton instance.
*
* CRITICAL: This returns undefined in OSS builds. There is no telemetry provider
* CRITICAL: This returns null in OSS builds. There is no telemetry service
* for OSS builds and all tracking calls are no-ops.
*/
export function useTelemetry(): TelemetryProvider | null {
if (_telemetryProvider === null) {
export function useTelemetry(): TelemetryService | null {
if (_telemetryService === null) {
// Use distribution check for tree-shaking
if (isCloud) {
_telemetryProvider = new MixpanelTelemetryProvider()
_telemetryService = new TelemetryService()

// Initialize and add both analytics providers
const mixpanelProvider = new MixpanelTelemetryProvider()
const cloudAnalyticsProvider = new CloudAnalyticsProvider()

void mixpanelProvider.initialize().then(() => {
_telemetryService?.addProvider(mixpanelProvider)
})

void cloudAnalyticsProvider.initialize().then(() => {
_telemetryService?.addProvider(cloudAnalyticsProvider)

Check failure on line 48 in src/platform/telemetry/index.ts

View workflow job for this annotation

GitHub Actions / collect

Argument of type 'CloudAnalyticsProvider' is not assignable to parameter of type 'TelemetryProvider'.

Check failure on line 48 in src/platform/telemetry/index.ts

View workflow job for this annotation

GitHub Actions / setup

Argument of type 'CloudAnalyticsProvider' is not assignable to parameter of type 'TelemetryProvider'.
})
}
// For OSS builds, _telemetryProvider stays null
// For OSS builds, _telemetryService stays null
}

return _telemetryProvider
return _telemetryService
}
49 changes: 49 additions & 0 deletions src/platform/telemetry/interfaces/TelemetryHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type {
ExecutionContext,
SurveyResponses,
TemplateMetadata
} from '../types'

/**
* Context types provided by domain stores to telemetry
*/
interface UserContext {
id: string
email?: string
tier?: 'free' | 'pro' | 'enterprise'
}

interface WorkflowContext {
filename?: string
isTemplate: boolean
nodeCount?: number
hasCustomNodes?: boolean
}

interface SubscriptionContext {
isSubscribed: boolean
plan?: string
creditsRemaining?: number
}

/**
* Telemetry hooks interface for dependency inversion.
* Domain stores register these hooks to provide context to telemetry
* without creating circular dependencies.
*/
export interface TelemetryHooks {
// Context providers
getExecutionContext?(): ExecutionContext | null
getCurrentUser?(): UserContext | null
getActiveWorkflow?(): WorkflowContext | null

// Setting providers
getSurveyData?(): SurveyResponses | null
getSubscriptionStatus?(): SubscriptionContext | null

// Template metadata providers
getTemplateMetadata?(filename: string): TemplateMetadata | null

// Feature flag providers
getFeatureFlags?(): Record<string, boolean>
}
91 changes: 91 additions & 0 deletions src/platform/telemetry/providers/TelemetryProviderBase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type {
AuthMetadata,
ExecutionContext,
ExecutionErrorMetadata,
ExecutionSuccessMetadata,
HelpCenterClosedMetadata,
HelpCenterOpenedMetadata,
HelpResourceClickedMetadata,
NodeSearchMetadata,
NodeSearchResultMetadata,
PageVisibilityMetadata,
RunButtonProperties,
SettingChangedMetadata,
SurveyResponses,
TabCountMetadata,
TelemetryProvider,
TemplateFilterMetadata,
TemplateLibraryClosedMetadata,
TemplateLibraryMetadata,
TemplateMetadata,
UiButtonClickMetadata,
WorkflowCreatedMetadata,
WorkflowImportMetadata
} from '../types'

/**
* Abstract base class for telemetry providers with lifecycle management.
* Concrete providers should extend this class for consistent behavior.
*/
export abstract class TelemetryProviderBase implements TelemetryProvider {

Check failure on line 30 in src/platform/telemetry/providers/TelemetryProviderBase.ts

View workflow job for this annotation

GitHub Actions / collect

Class 'TelemetryProviderBase' incorrectly implements interface 'TelemetryProvider'.

Check failure on line 30 in src/platform/telemetry/providers/TelemetryProviderBase.ts

View workflow job for this annotation

GitHub Actions / setup

Class 'TelemetryProviderBase' incorrectly implements interface 'TelemetryProvider'.
protected isEnabled = true
protected isInitialized = false

/**
* Initialize the provider (e.g., load external libraries)
*/
abstract initialize(): Promise<void>

/**
* Check if the provider is ready to track events
*/
isReady(): boolean {
return this.isEnabled && this.isInitialized
}

setEnabled(enabled: boolean): void {
this.isEnabled = enabled
}

abstract trackAuth(metadata: AuthMetadata): void
abstract trackUserLoggedIn(): void
abstract trackSubscription(event: 'modal_opened' | 'subscribe_clicked'): void
abstract trackMonthlySubscriptionSucceeded(): void
abstract trackAddApiCreditButtonClicked(): void
abstract trackApiCreditTopupButtonPurchaseClicked(amount: number): void
abstract trackApiCreditTopupSucceeded(): void
abstract trackRunButton(properties: RunButtonProperties): void
abstract startTopupTracking(): void
abstract checkForCompletedTopup(events: any[] | undefined | null): boolean
abstract clearTopupTracking(): void
abstract trackSurvey(
stage: 'opened' | 'submitted',
responses?: SurveyResponses
): void
abstract trackEmailVerification(
stage: 'opened' | 'requested' | 'completed'
): void
abstract trackTemplate(metadata: TemplateMetadata): void
abstract trackTemplateLibraryOpened(metadata: TemplateLibraryMetadata): void
abstract trackTemplateLibraryClosed(
metadata: TemplateLibraryClosedMetadata
): void
abstract trackWorkflowImported(metadata: WorkflowImportMetadata): void
abstract trackWorkflowOpened(metadata: WorkflowImportMetadata): void
abstract trackPageVisibilityChanged(metadata: PageVisibilityMetadata): void
abstract trackTabCount(metadata: TabCountMetadata): void
abstract trackNodeSearch(metadata: NodeSearchMetadata): void
abstract trackNodeSearchResultSelected(
metadata: NodeSearchResultMetadata
): void
abstract trackTemplateFilterChanged(metadata: TemplateFilterMetadata): void
abstract trackHelpCenterOpened(metadata: HelpCenterOpenedMetadata): void
abstract trackHelpResourceClicked(metadata: HelpResourceClickedMetadata): void
abstract trackHelpCenterClosed(metadata: HelpCenterClosedMetadata): void
abstract trackWorkflowCreated(metadata: WorkflowCreatedMetadata): void
abstract trackWorkflowExecution(context?: ExecutionContext): void
abstract trackExecutionError(metadata: ExecutionErrorMetadata): void
abstract trackExecutionSuccess(metadata: ExecutionSuccessMetadata): void
abstract trackSettingChanged(metadata: SettingChangedMetadata): void
abstract trackUiButtonClicked(metadata: UiButtonClickMetadata): void
}
Loading
Loading