@servicenow/sdk-build-core 4.4.1 → 4.6.0
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/compression.d.ts +2 -1
- package/dist/compression.js +5 -2
- package/dist/compression.js.map +1 -1
- package/dist/keys-registry.d.ts +2 -1
- package/dist/keys-registry.js +15 -15
- package/dist/keys-registry.js.map +1 -1
- package/dist/now-config.d.ts +476 -5
- package/dist/now-config.js +165 -1
- package/dist/now-config.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/plugin.d.ts +17 -18
- package/dist/plugins/plugin.js +52 -31
- package/dist/plugins/plugin.js.map +1 -1
- package/dist/plugins/post-install.d.ts +31 -0
- package/dist/plugins/post-install.js +3 -0
- package/dist/plugins/post-install.js.map +1 -0
- package/dist/plugins/shape.js +4 -1
- package/dist/plugins/shape.js.map +1 -1
- package/dist/taxonomy.js +50 -0
- package/dist/taxonomy.js.map +1 -1
- package/dist/telemetry/clients/abstract-client.d.ts +23 -2
- package/dist/telemetry/clients/abstract-client.js +80 -0
- package/dist/telemetry/clients/abstract-client.js.map +1 -1
- package/dist/telemetry/clients/browser-client.d.ts +15 -9
- package/dist/telemetry/clients/browser-client.js +8 -82
- package/dist/telemetry/clients/browser-client.js.map +1 -1
- package/dist/telemetry/clients/node-client.d.ts +28 -6
- package/dist/telemetry/clients/node-client.js +37 -101
- package/dist/telemetry/clients/node-client.js.map +1 -1
- package/dist/telemetry/factory.js +4 -6
- package/dist/telemetry/factory.js.map +1 -1
- package/dist/telemetry/types.d.ts +1 -1
- package/dist/typescript.d.ts +8 -0
- package/dist/typescript.js +12 -1
- package/dist/typescript.js.map +1 -1
- package/dist/xml.js +3 -1
- package/dist/xml.js.map +1 -1
- package/now.config.schema.json +386 -2
- package/package.json +5 -6
- package/src/compression.ts +7 -2
- package/src/keys-registry.ts +14 -12
- package/src/now-config.ts +204 -1
- package/src/plugins/index.ts +1 -0
- package/src/plugins/plugin.ts +86 -63
- package/src/plugins/post-install.ts +34 -0
- package/src/plugins/shape.ts +4 -1
- package/src/taxonomy.ts +53 -0
- package/src/telemetry/clients/abstract-client.ts +104 -3
- package/src/telemetry/clients/browser-client.ts +12 -95
- package/src/telemetry/clients/node-client.ts +39 -114
- package/src/telemetry/factory.ts +5 -8
- package/src/telemetry/types.ts +2 -1
- package/src/typescript.ts +12 -1
- package/src/xml.ts +4 -1
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Telemetry,
|
|
3
|
+
TelemetryEvent,
|
|
4
|
+
TelemetryEventData,
|
|
5
|
+
TimerMetric,
|
|
6
|
+
InstanceSettings,
|
|
7
|
+
Session,
|
|
8
|
+
Datapoint,
|
|
9
|
+
HeartbeatBody,
|
|
10
|
+
} from '../types'
|
|
11
|
+
import { NETWORK_TIMEOUT_MS, DatapointType } from '../types'
|
|
2
12
|
import { AppSeeTimerMetric } from './util'
|
|
3
13
|
|
|
4
14
|
export abstract class AbstractAppSeeClient implements Telemetry {
|
|
@@ -7,6 +17,7 @@ export abstract class AbstractAppSeeClient implements Telemetry {
|
|
|
7
17
|
protected readonly hostname?: string | undefined
|
|
8
18
|
protected session?: Session
|
|
9
19
|
protected startPromise?: Promise<void>
|
|
20
|
+
protected userId?: string
|
|
10
21
|
|
|
11
22
|
constructor(
|
|
12
23
|
protected readonly config: InstanceSettings,
|
|
@@ -58,6 +69,96 @@ export abstract class AbstractAppSeeClient implements Telemetry {
|
|
|
58
69
|
])
|
|
59
70
|
}
|
|
60
71
|
|
|
61
|
-
protected
|
|
62
|
-
|
|
72
|
+
protected doSendEvent(events: TelemetryEvent | TelemetryEvent[]): void {
|
|
73
|
+
try {
|
|
74
|
+
if (!this.session) {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const defaultValues = this.getDefaultDataValues()
|
|
79
|
+
const eventData = Array.isArray(events) ? events : [events]
|
|
80
|
+
const now = Date.now()
|
|
81
|
+
const hostname = this.hostname ? { hostname: this.hostname } : {}
|
|
82
|
+
|
|
83
|
+
const dataPoints = eventData.map<Datapoint>((d) => ({
|
|
84
|
+
t: DatapointType.Event,
|
|
85
|
+
d: now,
|
|
86
|
+
n: d.name,
|
|
87
|
+
p: { scopeId: d.appInfo?.scopeId, ...hostname, ...defaultValues, ...d.data },
|
|
88
|
+
}))
|
|
89
|
+
|
|
90
|
+
dataPoints.push({
|
|
91
|
+
t: DatapointType.User,
|
|
92
|
+
d: now,
|
|
93
|
+
n: this.userId ?? 'unknown',
|
|
94
|
+
p: this.getUserProps(hostname),
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const body: HeartbeatBody = {
|
|
98
|
+
SessionId: this.session.SessionId,
|
|
99
|
+
DataPoints: dataPoints,
|
|
100
|
+
TabId: this.session.TabId || '0',
|
|
101
|
+
ClientTime: new Date().toISOString(),
|
|
102
|
+
ConfigReceivedTime: new Date().toISOString(),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fetch(new URL('/web/heartbeat', this.config.BaseUrl), {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: this.getHeaders(),
|
|
108
|
+
body: JSON.stringify(body),
|
|
109
|
+
signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
|
|
110
|
+
}).catch(() => {})
|
|
111
|
+
} catch (_error) {}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected getHeaders() {
|
|
115
|
+
return {
|
|
116
|
+
APIKey: this.config.APIKey,
|
|
117
|
+
APIAuth: this.config.APIAuth,
|
|
118
|
+
BrowserId: '0',
|
|
119
|
+
ClientId: this.session?.ClientId || '0',
|
|
120
|
+
Version: this.sdkVersion || 'unknown',
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
protected async doStart(): Promise<void> {
|
|
126
|
+
try {
|
|
127
|
+
if (this.session) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.userId = await this.getUserHash()
|
|
132
|
+
|
|
133
|
+
const tabId = this.generateHexString()
|
|
134
|
+
const body = {
|
|
135
|
+
RequestId: this.generateHexString(),
|
|
136
|
+
TabId: tabId,
|
|
137
|
+
AppUserId: this.userId,
|
|
138
|
+
ClientTime: new Date().toISOString(),
|
|
139
|
+
TrackingLevel: 'Full',
|
|
140
|
+
...this.getEnvironmentInfo(),
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const response = await fetch(new URL('/web/config', this.config.BaseUrl), {
|
|
144
|
+
method: 'POST',
|
|
145
|
+
headers: this.getHeaders(),
|
|
146
|
+
body: JSON.stringify(body),
|
|
147
|
+
signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const bodyResponse = await response.json()
|
|
151
|
+
if (response.ok) {
|
|
152
|
+
this.session = { ...bodyResponse, TabId: tabId }
|
|
153
|
+
} else {
|
|
154
|
+
throw new Error(`Failed to start session with status ${response.status}: ${bodyResponse.Error}`)
|
|
155
|
+
}
|
|
156
|
+
} catch (_error) {}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
abstract getUserHash(): Promise<string>
|
|
160
|
+
abstract generateHexString(): string
|
|
161
|
+
abstract getEnvironmentInfo(): { SystemLocale: string; ScreenHeight: number; ScreenWidth: number }
|
|
162
|
+
abstract getDefaultDataValues(): TelemetryEventData<{ version: string; clientName: string }>
|
|
163
|
+
abstract getUserProps(hostname: Record<string, string>): TelemetryEventData
|
|
63
164
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { NETWORK_TIMEOUT_MS, DatapointType, type InstanceSettings } from '../types'
|
|
1
|
+
import type { TelemetryEventData, InstanceSettings } from '../types'
|
|
3
2
|
import { AbstractAppSeeClient } from './abstract-client'
|
|
4
3
|
|
|
5
4
|
export class BrowserTelemetryClient extends AbstractAppSeeClient {
|
|
@@ -22,9 +21,8 @@ export class BrowserTelemetryClient extends AbstractAppSeeClient {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
private readonly ideVersion: string | undefined
|
|
25
|
-
private userId?: string
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
override async getUserHash(): Promise<string> {
|
|
28
26
|
const userUniqueId = await this.getUserIdentifier()
|
|
29
27
|
const bytes = new TextEncoder().encode(userUniqueId)
|
|
30
28
|
const byteHash = await crypto.subtle.digest('SHA-256', bytes)
|
|
@@ -52,109 +50,28 @@ export class BrowserTelemetryClient extends AbstractAppSeeClient {
|
|
|
52
50
|
}
|
|
53
51
|
}
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
override generateHexString(): string {
|
|
56
54
|
return crypto.randomUUID()
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
override
|
|
60
|
-
try {
|
|
61
|
-
if (this.session) {
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
this.userId = await this.getUserHash()
|
|
66
|
-
|
|
67
|
-
const tabId = this.generateHexString()
|
|
68
|
-
const body = {
|
|
69
|
-
RequestId: this.generateHexString(),
|
|
70
|
-
TabId: tabId,
|
|
71
|
-
SystemLocale: navigator.language ?? 'unknown',
|
|
72
|
-
AppUserId: this.userId,
|
|
73
|
-
ScreenHeight: typeof screen !== 'undefined' ? screen.height : 0,
|
|
74
|
-
ScreenWidth: typeof screen !== 'undefined' ? screen.width : 0,
|
|
75
|
-
ClientTime: new Date().toISOString(),
|
|
76
|
-
TrackingLevel: 'Full',
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const response = await fetch(new URL('/web/config', this.config.BaseUrl), {
|
|
80
|
-
method: 'POST',
|
|
81
|
-
headers: this.getHeaders(),
|
|
82
|
-
body: JSON.stringify(body),
|
|
83
|
-
signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
const bodyResponse = await response.json()
|
|
87
|
-
if (response.ok) {
|
|
88
|
-
this.session = { ...bodyResponse, TabId: tabId }
|
|
89
|
-
} else {
|
|
90
|
-
throw new Error(`Failed to start session with status ${response.status}: ${bodyResponse.Error}`)
|
|
91
|
-
}
|
|
92
|
-
} catch (_error) {}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
override doSendEvent(events: TelemetryEvent | TelemetryEvent[]): void {
|
|
96
|
-
try {
|
|
97
|
-
if (!this.session) {
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const defaultValues = this.getDefaultDataValues()
|
|
102
|
-
const eventData = Array.isArray(events) ? events : [events]
|
|
103
|
-
const now = Date.now()
|
|
104
|
-
const hostname = this.hostname ? { hostname: this.hostname } : {}
|
|
105
|
-
|
|
106
|
-
const dataPoints = eventData.map<Datapoint>((d) => ({
|
|
107
|
-
t: DatapointType.Event,
|
|
108
|
-
d: now,
|
|
109
|
-
n: d.name,
|
|
110
|
-
p: { scopeId: d.appInfo?.scopeId, ...hostname, ...defaultValues, ...d.data },
|
|
111
|
-
}))
|
|
112
|
-
|
|
113
|
-
const userProps = { hostname } //does CI make sense for browser environment ??
|
|
114
|
-
|
|
115
|
-
dataPoints.push({
|
|
116
|
-
t: DatapointType.User,
|
|
117
|
-
d: now,
|
|
118
|
-
n: this.userId!,
|
|
119
|
-
p: userProps,
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
const body: HeartbeatBody = {
|
|
123
|
-
SessionId: this.session.SessionId,
|
|
124
|
-
DataPoints: dataPoints,
|
|
125
|
-
TabId: this.session.TabId || '0',
|
|
126
|
-
ClientTime: new Date().toISOString(),
|
|
127
|
-
ConfigReceivedTime: new Date().toISOString(),
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
fetch(new URL('/web/heartbeat', this.config.BaseUrl), {
|
|
131
|
-
method: 'POST',
|
|
132
|
-
headers: this.getHeaders(),
|
|
133
|
-
body: JSON.stringify(body),
|
|
134
|
-
signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
|
|
135
|
-
}).catch(() => {})
|
|
136
|
-
} catch (_error) {}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private getHeaders() {
|
|
57
|
+
override getEnvironmentInfo() {
|
|
140
58
|
return {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
ClientId: this.session?.ClientId || '0',
|
|
145
|
-
Version: this.sdkVersion || 'unknown',
|
|
146
|
-
'Content-Type': 'application/json',
|
|
59
|
+
SystemLocale: navigator.language ?? 'unknown',
|
|
60
|
+
ScreenHeight: typeof screen !== 'undefined' ? screen.height : 0,
|
|
61
|
+
ScreenWidth: typeof screen !== 'undefined' ? screen.width : 0,
|
|
147
62
|
}
|
|
148
63
|
}
|
|
149
64
|
|
|
150
|
-
|
|
151
|
-
|
|
65
|
+
override getDefaultDataValues() {
|
|
66
|
+
return {
|
|
152
67
|
browser: navigator.userAgent || 'unknown',
|
|
153
68
|
version: this.sdkVersion || 'unknown',
|
|
154
69
|
clientName: this.clientName,
|
|
155
70
|
ideVersion: this.ideVersion || 'unknown',
|
|
156
71
|
}
|
|
72
|
+
}
|
|
157
73
|
|
|
158
|
-
|
|
74
|
+
override getUserProps(hostname: Record<string, string>): TelemetryEventData {
|
|
75
|
+
return { ...hostname }
|
|
159
76
|
}
|
|
160
77
|
}
|
|
@@ -1,26 +1,8 @@
|
|
|
1
1
|
import * as os from 'os'
|
|
2
2
|
import ciInfo from 'ci-info'
|
|
3
|
-
import {
|
|
4
|
-
import type { TelemetryEvent, TelemetryEventData, Datapoint, HeartbeatBody, InstanceSettings } from '../types'
|
|
3
|
+
import type { TelemetryEventData, InstanceSettings } from '../types'
|
|
5
4
|
import { AbstractAppSeeClient } from './abstract-client'
|
|
6
5
|
|
|
7
|
-
function generateHexString() {
|
|
8
|
-
// biome-ignore lint/style/noRestrictedImports: <explanation>
|
|
9
|
-
const crypto = require('node:crypto')
|
|
10
|
-
return crypto.randomBytes(16).toString('hex')
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function getUserHash() {
|
|
14
|
-
const user = `${os.userInfo().username}:${os.hostname()}`
|
|
15
|
-
if (!user) {
|
|
16
|
-
return 'unknown'
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const bytes = new TextEncoder().encode(user)
|
|
20
|
-
const byteHash = await crypto.subtle.digest('SHA-256', bytes)
|
|
21
|
-
return Buffer.from(byteHash).toString('hex')
|
|
22
|
-
}
|
|
23
|
-
|
|
24
6
|
export class NodeTelemetryClient extends AbstractAppSeeClient {
|
|
25
7
|
constructor(
|
|
26
8
|
config: InstanceSettings,
|
|
@@ -32,120 +14,63 @@ export class NodeTelemetryClient extends AbstractAppSeeClient {
|
|
|
32
14
|
})
|
|
33
15
|
}
|
|
34
16
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (this.session) {
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
this.userId = await getUserHash()
|
|
44
|
-
|
|
45
|
-
const tabId = generateHexString()
|
|
46
|
-
const body = {
|
|
47
|
-
RequestId: generateHexString(),
|
|
48
|
-
TabId: tabId,
|
|
49
|
-
SystemLocale: (process.env['LANG'] || 'unknown').split('.')[0],
|
|
50
|
-
AppUserId: this.userId,
|
|
51
|
-
ScreenHeight: 0,
|
|
52
|
-
ScreenWidth: 0,
|
|
53
|
-
ClientTime: new Date().toISOString(),
|
|
54
|
-
TrackingLevel: 'Full',
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const response = await fetch(new URL('/web/config', this.config.BaseUrl), {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
headers: this.getHeaders(),
|
|
60
|
-
body: JSON.stringify(body),
|
|
61
|
-
signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const bodyResponse = await response.json()
|
|
65
|
-
if (response.ok) {
|
|
66
|
-
this.session = { ...bodyResponse, TabId: tabId }
|
|
67
|
-
} else {
|
|
68
|
-
throw new Error(`Failed to start session with status ${response.status}: ${bodyResponse.Error}`)
|
|
69
|
-
}
|
|
70
|
-
} catch (_error) {
|
|
71
|
-
//ignore
|
|
17
|
+
override async getUserHash(): Promise<string> {
|
|
18
|
+
const user = `${os.userInfo().username}:${os.hostname()}`
|
|
19
|
+
if (!user) {
|
|
20
|
+
return 'unknown'
|
|
72
21
|
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
override doSendEvent(events: TelemetryEvent | TelemetryEvent[]): void {
|
|
76
|
-
try {
|
|
77
|
-
//Check if we have a session
|
|
78
|
-
if (!this.session) {
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
22
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const dataPoints = eventData.map<Datapoint>((d) => ({
|
|
88
|
-
t: DatapointType.Event,
|
|
89
|
-
d: now,
|
|
90
|
-
n: d.name,
|
|
91
|
-
p: { scopeId: d.appInfo?.scopeId, ...hostname, ...defaultValues, ...d.data },
|
|
92
|
-
}))
|
|
93
|
-
|
|
94
|
-
const userProps = this.applyCITelemetry({ ...hostname })
|
|
95
|
-
|
|
96
|
-
dataPoints.push({
|
|
97
|
-
t: DatapointType.User,
|
|
98
|
-
d: now,
|
|
99
|
-
n: this.userId!,
|
|
100
|
-
p: userProps,
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
const body: HeartbeatBody = {
|
|
104
|
-
SessionId: this.session.SessionId,
|
|
105
|
-
DataPoints: dataPoints,
|
|
106
|
-
TabId: this.session.TabId || '0',
|
|
107
|
-
ClientTime: new Date().toISOString(),
|
|
108
|
-
ConfigReceivedTime: new Date().toISOString(),
|
|
109
|
-
}
|
|
23
|
+
const bytes = new TextEncoder().encode(user)
|
|
24
|
+
const byteHash = await crypto.subtle.digest('SHA-256', bytes)
|
|
25
|
+
return Buffer.from(byteHash).toString('hex')
|
|
26
|
+
}
|
|
110
27
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
|
|
116
|
-
}).catch(() => {})
|
|
117
|
-
} catch (_error) {
|
|
118
|
-
//ignore errors from telemetry
|
|
119
|
-
}
|
|
28
|
+
override generateHexString(): string {
|
|
29
|
+
// biome-ignore lint/style/noRestrictedImports: node:crypto is not available via sdk-build-core
|
|
30
|
+
const nodeCrypto = require('node:crypto')
|
|
31
|
+
return nodeCrypto.randomBytes(16).toString('hex')
|
|
120
32
|
}
|
|
121
33
|
|
|
122
|
-
|
|
34
|
+
override getEnvironmentInfo() {
|
|
123
35
|
return {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
ClientId: this.session?.ClientId || '0',
|
|
128
|
-
Version: this.sdkVersion || 'unknown',
|
|
129
|
-
'Content-Type': 'application/json',
|
|
36
|
+
SystemLocale: (process.env['LANG'] || 'unknown').split('.')[0] ?? 'unknown',
|
|
37
|
+
ScreenHeight: 0,
|
|
38
|
+
ScreenWidth: 0,
|
|
130
39
|
}
|
|
131
40
|
}
|
|
132
41
|
|
|
133
|
-
|
|
134
|
-
const
|
|
42
|
+
override getDefaultDataValues() {
|
|
43
|
+
const codingAgentData: TelemetryEventData<{ codingAgent?: string }> = {}
|
|
44
|
+
if (process.env['GEMINI_CLI']) {
|
|
45
|
+
codingAgentData.codingAgent = 'gemini'
|
|
46
|
+
} else if (process.env['CLAUDECODE']) {
|
|
47
|
+
codingAgentData.codingAgent = 'claudecode'
|
|
48
|
+
} else if (process.env['CODEX_CI']) {
|
|
49
|
+
codingAgentData.codingAgent = 'codex'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
135
53
|
os: os.type(),
|
|
136
54
|
version: this.sdkVersion || 'unknown',
|
|
137
55
|
nodeVersion: process.version,
|
|
138
56
|
clientName: this.clientName,
|
|
57
|
+
...codingAgentData,
|
|
58
|
+
...this.applyCITelemetry(),
|
|
139
59
|
}
|
|
60
|
+
}
|
|
140
61
|
|
|
141
|
-
|
|
62
|
+
override getUserProps(hostname: Record<string, string>): TelemetryEventData {
|
|
63
|
+
return {
|
|
64
|
+
hostname,
|
|
65
|
+
...this.applyCITelemetry(),
|
|
66
|
+
}
|
|
142
67
|
}
|
|
143
68
|
|
|
144
|
-
private applyCITelemetry(
|
|
69
|
+
private applyCITelemetry() {
|
|
145
70
|
if (ciInfo.isCI) {
|
|
146
|
-
return {
|
|
71
|
+
return { ci: true, ciName: ciInfo.name, ciPR: ciInfo.isPR }
|
|
147
72
|
}
|
|
148
73
|
|
|
149
|
-
return
|
|
74
|
+
return {}
|
|
150
75
|
}
|
|
151
76
|
}
|
package/src/telemetry/factory.ts
CHANGED
|
@@ -10,16 +10,13 @@ export class TelemetryFactory {
|
|
|
10
10
|
type?: 'node' | 'browser'
|
|
11
11
|
attributes?: { sdkVersion?: string; clientName?: string; hostname?: string; ideVersion?: string }
|
|
12
12
|
}): Telemetry {
|
|
13
|
+
if (process.env['NODE_ENV'] === 'test' || !options || !options.type) {
|
|
14
|
+
return new NoOpTelemetry()
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
const config = getAppSeeConfig()
|
|
14
18
|
|
|
15
|
-
if (
|
|
16
|
-
process.env['NODE_ENV'] === 'test' ||
|
|
17
|
-
!options ||
|
|
18
|
-
!options.type ||
|
|
19
|
-
!config.APIKey ||
|
|
20
|
-
!config.APIAuth ||
|
|
21
|
-
!config.BaseUrl
|
|
22
|
-
) {
|
|
19
|
+
if (!config.APIKey || !config.APIAuth || !config.BaseUrl) {
|
|
23
20
|
return new NoOpTelemetry()
|
|
24
21
|
}
|
|
25
22
|
|
package/src/telemetry/types.ts
CHANGED
|
@@ -56,6 +56,7 @@ export interface TimerMetric {
|
|
|
56
56
|
end(eventData?: Pick<TelemetryEvent, 'appInfo' | 'data'>): TelemetryEvent
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export type TelemetryEventData = Record<string, unknown
|
|
59
|
+
export type TelemetryEventData<T extends Record<string, unknown> = Record<string, unknown>> = T &
|
|
60
|
+
Record<string, unknown>
|
|
60
61
|
|
|
61
62
|
export type TelemetryEvent = { name: string; appInfo?: { scopeId?: string }; data?: TelemetryEventData }
|
package/src/typescript.ts
CHANGED
|
@@ -159,6 +159,10 @@ export const SupportedKinds = {
|
|
|
159
159
|
name: 'FunctionExpression',
|
|
160
160
|
node: {} as ts.FunctionExpression,
|
|
161
161
|
},
|
|
162
|
+
[ts.SyntaxKind.ParenthesizedExpression]: {
|
|
163
|
+
name: 'ParenthesizedExpression',
|
|
164
|
+
node: {} as ts.ParenthesizedExpression,
|
|
165
|
+
},
|
|
162
166
|
[ts.SyntaxKind.FunctionDeclaration]: {
|
|
163
167
|
name: 'FunctionDeclaration',
|
|
164
168
|
node: {} as ts.FunctionDeclaration,
|
|
@@ -351,6 +355,10 @@ export const SupportedKinds = {
|
|
|
351
355
|
name: 'PrefixUnaryExpression',
|
|
352
356
|
node: {} as ts.PrefixUnaryExpression,
|
|
353
357
|
},
|
|
358
|
+
[ts.SyntaxKind.ReturnStatement]: {
|
|
359
|
+
name: 'ReturnStatement',
|
|
360
|
+
node: {} as ts.ReturnStatement,
|
|
361
|
+
},
|
|
354
362
|
} satisfies {
|
|
355
363
|
[K in ts.SyntaxKind]?: {
|
|
356
364
|
name: keyof typeof ts.SyntaxKind
|
|
@@ -475,7 +483,10 @@ export function remove(target: ts.Node): void {
|
|
|
475
483
|
}
|
|
476
484
|
}
|
|
477
485
|
|
|
478
|
-
|
|
486
|
+
// It is possible that multiple plugins could attempt to remove the same node. To support this, only error if target is unsupported
|
|
487
|
+
if (!isSupportedNode(target)) {
|
|
488
|
+
throw new Error(`Unable to remove "${getUnsupportedKindName(target)}" node: ${target.getText()}`)
|
|
489
|
+
}
|
|
479
490
|
}
|
|
480
491
|
|
|
481
492
|
/**
|
package/src/xml.ts
CHANGED
|
@@ -104,8 +104,11 @@ export function recordXml(
|
|
|
104
104
|
But when fluent generates the XML from source it creates XML with which makes the XML invalid since `nbsp` is not defined.
|
|
105
105
|
|
|
106
106
|
So we are replacing with &nbsp; to ensure the XML is valid.
|
|
107
|
+
Note: This only applies to plain text content. CDATA sections do not parse
|
|
108
|
+
entity references, so is valid as-is inside CDATA.
|
|
107
109
|
*/
|
|
108
|
-
const sanitizedValue =
|
|
110
|
+
const sanitizedValue =
|
|
111
|
+
contentType === 'plain' ? stringValue.replaceAll(' ', '&nbsp;') : stringValue
|
|
109
112
|
|
|
110
113
|
const columnNameElement: Element = {
|
|
111
114
|
type: 'element',
|