33 * SPDX-License-Identifier: Apache-2.0
44 */
55
6+ /* eslint-disable aws-toolkits/no-console-log */
7+
68import * as vscode from 'vscode'
79import { env , version } from 'vscode'
810import * as os from 'os'
@@ -28,7 +30,7 @@ import { randomUUID } from '../crypto'
2830import { ClassToInterfaceType } from '../utilities/tsUtils'
2931import { FunctionEntry , type TelemetryTracer } from './spans'
3032import { telemetry } from './telemetry'
31- import { v5 as uuidV5 } from 'uuid'
33+ import { v4 as uuidV4 , v5 as uuidV5 } from 'uuid'
3234
3335const legacySettingsTelemetryValueDisable = 'Disable'
3436const legacySettingsTelemetryValueEnable = 'Enable'
@@ -98,33 +100,62 @@ export function convertLegacy(value: unknown): boolean {
98100 * can be used in conjunction with the client ID to differntiate between
99101 * different VS Code windows on a users machine.
100102 *
101- * ### Rules:
102- *
103- * - On startup of a new application instance, a new session ID must be created.
104- * - This identifier must be a `UUID`, it is enforced by the telemetry service.
105- * - The session ID must be different from all other IDs on the machine
106- * - A session ID exists until the application instance is terminated.
107- * It should never be used again after termination.
108- * - All extensions on the same application instance MUST return the same session ID.
109- * - This will allow us to know which of our extensions (eg Q vs Toolkit) are in the
110- * same VS Code window.
111- *
112- * `vscode.env.sessionId` behaves as described aboved, EXCEPT its
113- * value looks close to a UUID by does not exactly match it (has additional characters).
114- * As a result we process it through uuidV5 which creates a proper UUID from it.
115- * uuidV5 is idempotent, so as long as `vscode.env.sessionId` returns the same value,
116- * we will get the same UUID.
117- */
118- export const getSessionId = once ( ( ) => uuidV5 ( vscode . env . sessionId , sessionIdNonce ) )
119- /**
120- * This is an arbitrary nonce that is used in creating a v5 UUID for Session ID. We only
121- * have this since the spec requires it.
122- * - This should ONLY be used by {@link getSessionId}.
123- * - This value MUST NOT change during runtime, otherwise {@link getSessionId} will lose its
124- * idempotency. But, if there was a reason to change the value in a PR, it would not be an issue.
125- * - This is exported only for testing.
103+ * See spec: https://quip-amazon.com/9gqrAqwO5FCE
126104 */
127- export const sessionIdNonce = '44cfdb20-b30b-4585-a66c-9f48f24f99b5' as const
105+ export const getSessionId = once ( ( ) => SessionId . getSessionId ( ) )
106+
107+ class SessionId {
108+ public static getSessionId ( ) {
109+ // This implementation does not work in web
110+ if ( ! isWeb ( ) ) {
111+ return this . _getSessionId ( )
112+ }
113+ // A best effort at a sessionId just for web mode
114+ return this . _getVscSessionId ( )
115+ }
116+
117+ /**
118+ * This implementation assumes that the `globalThis` is shared between extensions in the same
119+ * Extension Host, so we can share a global variable that way.
120+ *
121+ * This does not seem to work on web mode since the `globalThis` is not shared due to WebWorker design
122+ */
123+ private static _getSessionId ( ) {
124+ const global = globalThis as any
125+ getLogger ( ) . debug ( 'getSessionId: global.amzn_sessionId=%s' , global . amzn_sessionId )
126+ if ( global . amzn_sessionId === undefined ) {
127+ getLogger ( ) . debug ( 'getSessionId: creating new sessionId' )
128+ ; ( globalThis as any ) . amzn_sessionId = uuidV4 ( )
129+ }
130+ getLogger ( ) . debug ( 'getSessionId: returning %s' , global . amzn_sessionId )
131+ return global . amzn_sessionId
132+ }
133+
134+ /**
135+ * `vscode.env.sessionId` looks close to a UUID by does not exactly match it (has additional characters).
136+ * As a result we process it through uuidV5 which creates a proper UUID from it.
137+ * uuidV5 is idempotent, so as long as `vscode.env.sessionId` returns the same value,
138+ * we will get the same UUID.
139+ *
140+ * We were initially using this implementation for all session ids, but it has some caveats:
141+ * - If the extension host crashes, sesionId stays the same since the parent VSC process defines it and that does not crash.
142+ * We wanted it to generate a new sessionId on ext host crash.
143+ * - This value may not be reliable, see the following sessionId in telemetry, it contains many events
144+ * all from different client ids: `sessionId: cabea8e7-a8a1-5e51-a60e-07218f4a5937`
145+ */
146+ private static _getVscSessionId ( ) {
147+ return uuidV5 ( vscode . env . sessionId , this . sessionIdNonce )
148+ }
149+ /**
150+ * This is an arbitrary nonce that is used in creating a v5 UUID for Session ID. We only
151+ * have this since the spec requires it.
152+ * - This should ONLY be used by {@link getSessionId}.
153+ * - This value MUST NOT change during runtime, otherwise {@link getSessionId} will lose its
154+ * idempotency. But, if there was a reason to change the value in a PR, it would not be an issue.
155+ * - This is exported only for testing.
156+ */
157+ private static readonly sessionIdNonce = '44cfdb20-b30b-4585-a66c-9f48f24f99b5'
158+ }
128159
129160/**
130161 * Calculates the clientId for the current profile. This calculation is performed once
0 commit comments