@@ -25,33 +25,36 @@ function getTelemetryFlagFilePath(): string {
25
25
}
26
26
27
27
/**
28
- * Atomically attempts to create a telemetry flag file to prevent duplicate events.
29
- * This uses the 'wx' flag to ensure only one process can successfully create the file.
30
- *
31
- * @returns true if this process won the race and created the flag file, false if it already exists
28
+ * Checks if the telemetry event has already been fired by looking for a flag file.
32
29
*/
33
- async function tryCreateTelemetryFlag ( ) : Promise < boolean > {
30
+ async function hasTelemetryEventBeenFired ( ) : Promise < boolean > {
31
+ try {
32
+ await fs . access ( getTelemetryFlagFilePath ( ) ) ;
33
+ return true ;
34
+ } catch {
35
+ return false ;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Creates a flag file to mark that the telemetry event has been fired.
41
+ */
42
+ async function markTelemetryEventAsFired ( ) : Promise < void > {
34
43
try {
35
44
// Ensure the directory exists
36
45
const dir = join ( process . cwd ( ) , '.clerk/.tmp' ) ;
37
46
await fs . mkdir ( dir , { recursive : true } ) ;
38
47
39
- // Create the flag file with timestamp using 'wx' flag for atomic creation
48
+ // Create the flag file with timestamp
40
49
const flagData = {
41
50
firedAt : new Date ( ) . toISOString ( ) ,
42
51
event : EVENT_KEYLESS_ENV_DRIFT_DETECTED ,
43
52
} ;
44
53
45
- await fs . writeFile ( getTelemetryFlagFilePath ( ) , JSON . stringify ( flagData , null , 2 ) , { flag : 'wx' } ) ;
46
- return true ; // Successfully created the file - we won the race
47
- } catch ( error : any ) {
48
- if ( error . code === 'EEXIST' ) {
49
- // File already exists - another process won the race
50
- return false ;
51
- }
52
- // Other errors (permission issues, etc.) - silently handle to avoid breaking the application
54
+ await fs . writeFile ( getTelemetryFlagFilePath ( ) , JSON . stringify ( flagData , null , 2 ) ) ;
55
+ } catch ( error ) {
56
+ // Silently handle errors to avoid breaking the application
53
57
console . warn ( 'Failed to create telemetry flag file:' , error ) ;
54
- return false ;
55
58
}
56
59
}
57
60
@@ -74,6 +77,11 @@ export async function detectKeylessEnvDrift(): Promise<void> {
74
77
}
75
78
76
79
try {
80
+ // Check if telemetry event has already been fired
81
+ if ( await hasTelemetryEventBeenFired ( ) ) {
82
+ return ;
83
+ }
84
+
77
85
// Dynamically import server-side dependencies to avoid client-side issues
78
86
const { safeParseClerkFile } = await import ( './keyless-node.js' ) ;
79
87
@@ -93,7 +101,7 @@ export async function detectKeylessEnvDrift(): Promise<void> {
93
101
const keylessFileHasKeys = Boolean ( keylessFile ?. publishableKey && keylessFile ?. secretKey ) ;
94
102
95
103
if ( envVarsMissing && keylessFileHasKeys ) {
96
- // Environment variables are missing and keyless file has keys. This is expected for keyless mode
104
+ // Environment variables are missing but keyless file has keys - this is normal for keyless mode
97
105
return ;
98
106
}
99
107
@@ -106,23 +114,22 @@ export async function detectKeylessEnvDrift(): Promise<void> {
106
114
// Check if there's a drift (mismatch between env vars and keyless file)
107
115
const hasDrift = ! publicKeyMatch || ! secretKeyMatch ;
108
116
109
- // Only emit telemetry if there's drift and we can atomically create the flag file
110
- if ( hasDrift && ( await tryCreateTelemetryFlag ( ) ) ) {
111
- const payload : EventKeylessEnvDriftPayload = {
112
- publicKeyMatch,
113
- secretKeyMatch,
114
- envVarsMissing,
115
- keylessFileHasKeys,
116
- keylessPublishableKey : keylessFile . publishableKey ,
117
- envPublishableKey : envPublishableKey as string ,
118
- } ;
117
+ const payload : EventKeylessEnvDriftPayload = {
118
+ publicKeyMatch,
119
+ secretKeyMatch,
120
+ envVarsMissing,
121
+ keylessFileHasKeys,
122
+ keylessPublishableKey : keylessFile . publishableKey ,
123
+ envPublishableKey : envPublishableKey as string ,
124
+ } ;
119
125
120
- // Create a clerk client to access telemetry
121
- const clerkClient = createClerkClientWithOptions ( {
122
- publishableKey : keylessFile . publishableKey ,
123
- secretKey : keylessFile . secretKey ,
124
- } ) ;
126
+ // Create a clerk client to access telemetry
127
+ const clerkClient = createClerkClientWithOptions ( {
128
+ publishableKey : keylessFile . publishableKey ,
129
+ secretKey : keylessFile . secretKey ,
130
+ } ) ;
125
131
132
+ if ( hasDrift ) {
126
133
// Fire drift detected event
127
134
const driftDetectedEvent : TelemetryEventRaw < EventKeylessEnvDriftPayload > = {
128
135
event : EVENT_KEYLESS_ENV_DRIFT_DETECTED ,
@@ -131,6 +138,9 @@ export async function detectKeylessEnvDrift(): Promise<void> {
131
138
} ;
132
139
133
140
clerkClient . telemetry ?. record ( driftDetectedEvent ) ;
141
+
142
+ // Mark the telemetry event as fired to prevent future executions
143
+ await markTelemetryEventAsFired ( ) ;
134
144
}
135
145
} catch ( error ) {
136
146
// Silently handle errors to avoid breaking the application
0 commit comments