1
1
import type { TelemetryEventRaw } from '@clerk/types' ;
2
+ import { promises as fs } from 'fs' ;
3
+ import { join } from 'path' ;
2
4
3
5
import { createClerkClientWithOptions } from './createClerkClient' ;
4
6
5
7
const EVENT_KEYLESS_ENV_DRIFT_DETECTED = 'KEYLESS_ENV_DRIFT_DETECTED' ;
6
8
const EVENT_SAMPLING_RATE = 1 ; // 100% sampling rate
9
+ const TELEMETRY_FLAG_FILE = '.clerk/.tmp/keyless-telemetry-fired.json' ;
7
10
8
11
type EventKeylessEnvDriftPayload = {
9
12
publicKeyMatch : boolean ;
@@ -14,6 +17,47 @@ type EventKeylessEnvDriftPayload = {
14
17
envPublishableKey : string ;
15
18
} ;
16
19
20
+ /**
21
+ * Gets the absolute path to the telemetry flag file.
22
+ */
23
+ function getTelemetryFlagFilePath ( ) : string {
24
+ return join ( process . cwd ( ) , TELEMETRY_FLAG_FILE ) ;
25
+ }
26
+
27
+ /**
28
+ * Checks if the telemetry event has already been fired by looking for a flag file.
29
+ */
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 > {
43
+ try {
44
+ // Ensure the directory exists
45
+ const dir = join ( process . cwd ( ) , '.clerk/.tmp' ) ;
46
+ await fs . mkdir ( dir , { recursive : true } ) ;
47
+
48
+ // Create the flag file with timestamp
49
+ const flagData = {
50
+ firedAt : new Date ( ) . toISOString ( ) ,
51
+ event : EVENT_KEYLESS_ENV_DRIFT_DETECTED ,
52
+ } ;
53
+
54
+ await fs . writeFile ( getTelemetryFlagFilePath ( ) , JSON . stringify ( flagData , null , 2 ) ) ;
55
+ } catch ( error ) {
56
+ // Silently handle errors to avoid breaking the application
57
+ console . warn ( 'Failed to create telemetry flag file:' , error ) ;
58
+ }
59
+ }
60
+
17
61
/**
18
62
* Detects environment variable drift for keyless Next.js applications and fires telemetry events.
19
63
*
@@ -23,6 +67,8 @@ type EventKeylessEnvDriftPayload = {
23
67
* If there's a mismatch, it fires a `KEYLESS_ENV_DRIFT_DETECTED` event.
24
68
* For local testing purposes, it also fires a `KEYLESS_ENV_DRIFT_NOT_DETECTED` event when
25
69
* keys exist and match the environment variables.
70
+ *
71
+ * The telemetry event will only fire once per application instance to avoid noise.
26
72
*/
27
73
export async function detectKeylessEnvDrift ( ) : Promise < void > {
28
74
// Only run on server side
@@ -31,6 +77,11 @@ export async function detectKeylessEnvDrift(): Promise<void> {
31
77
}
32
78
33
79
try {
80
+ // Check if telemetry event has already been fired
81
+ if ( await hasTelemetryEventBeenFired ( ) ) {
82
+ return ;
83
+ }
84
+
34
85
// Dynamically import server-side dependencies to avoid client-side issues
35
86
const { safeParseClerkFile } = await import ( './keyless-node.js' ) ;
36
87
@@ -87,6 +138,9 @@ export async function detectKeylessEnvDrift(): Promise<void> {
87
138
} ;
88
139
89
140
clerkClient . telemetry ?. record ( driftDetectedEvent ) ;
141
+
142
+ // Mark the telemetry event as fired to prevent future executions
143
+ await markTelemetryEventAsFired ( ) ;
90
144
}
91
145
} catch ( error ) {
92
146
// Silently handle errors to avoid breaking the application
0 commit comments