node-avo-inspector 1.2.0 → 2.0.0-beta.1
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/CHANGELOG.md +46 -0
- package/README.md +268 -4
- package/package.json +17 -1
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/settings.local.json +0 -11
- package/.claude/worktrees/release-1.1.1/LICENSE +0 -21
- package/.claude/worktrees/release-1.1.1/README.md +0 -69
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/index.js +0 -30
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/package.json +0 -15
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/yarn.lock +0 -6
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/dist/index.js +0 -34
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/index.ts +0 -30
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/package.json +0 -18
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/tsconfig.json +0 -15
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/yarn.lock +0 -11
- package/.claude/worktrees/release-1.1.1/package-lock.json +0 -4039
- package/.claude/worktrees/release-1.1.1/package.json +0 -48
- package/.claude/worktrees/release-1.1.1/reason/AvoInspector.rei +0 -11
- package/.claude/worktrees/release-1.1.1/reason/NodeAvoInspector.re +0 -27
- package/.claude/worktrees/release-1.1.1/src/AvoDeduplicator.ts +0 -146
- package/.claude/worktrees/release-1.1.1/src/AvoEncryption.ts +0 -90
- package/.claude/worktrees/release-1.1.1/src/AvoGuid.ts +0 -9
- package/.claude/worktrees/release-1.1.1/src/AvoInspector.ts +0 -383
- package/.claude/worktrees/release-1.1.1/src/AvoInspectorEnv.ts +0 -8
- package/.claude/worktrees/release-1.1.1/src/AvoNetworkCallsHandler.ts +0 -267
- package/.claude/worktrees/release-1.1.1/src/AvoSchemaParser.ts +0 -115
- package/.claude/worktrees/release-1.1.1/src/AvoStreamId.ts +0 -16
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEncryption.ts +0 -515
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecCache.ts +0 -242
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecFetcher.ts +0 -241
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoInspector.ts +0 -360
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoStreamId.ts +0 -72
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestDeduplicator.ts +0 -191
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestEventValidator.ts +0 -231
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandler.ts +0 -179
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandlerPublic.ts +0 -208
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestParsing.ts +0 -153
- package/.claude/worktrees/release-1.1.1/src/__tests__/constants.ts +0 -63
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecCache.ts +0 -109
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetchTypes.ts +0 -47
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetcher.ts +0 -106
- package/.claude/worktrees/release-1.1.1/src/eventSpec/EventValidator.ts +0 -74
- package/.claude/worktrees/release-1.1.1/src/index.ts +0 -2
- package/.claude/worktrees/release-1.1.1/src/utils.ts +0 -49
- package/.claude/worktrees/release-1.1.1/tsconfig.json +0 -13
- package/.claude/worktrees/release-1.1.1/yarn.lock +0 -2310
- package/.github/workflows/claude.yml +0 -55
- package/dist/__tests__/TestAvoEncryption.d.ts +0 -1
- package/dist/__tests__/TestAvoEncryption.js +0 -288
- package/dist/__tests__/TestAvoEventSpecCache.d.ts +0 -1
- package/dist/__tests__/TestAvoEventSpecCache.js +0 -194
- package/dist/__tests__/TestAvoEventSpecFetcher.d.ts +0 -1
- package/dist/__tests__/TestAvoEventSpecFetcher.js +0 -219
- package/dist/__tests__/TestAvoInspector.d.ts +0 -1
- package/dist/__tests__/TestAvoInspector.js +0 -291
- package/dist/__tests__/TestAvoStreamId.d.ts +0 -1
- package/dist/__tests__/TestAvoStreamId.js +0 -55
- package/dist/__tests__/TestDeduplicator.d.ts +0 -1
- package/dist/__tests__/TestDeduplicator.js +0 -102
- package/dist/__tests__/TestEventValidator.d.ts +0 -1
- package/dist/__tests__/TestEventValidator.js +0 -196
- package/dist/__tests__/TestNetworkCallsHandler.d.ts +0 -1
- package/dist/__tests__/TestNetworkCallsHandler.js +0 -86
- package/dist/__tests__/TestNetworkCallsHandlerPublic.d.ts +0 -1
- package/dist/__tests__/TestNetworkCallsHandlerPublic.js +0 -160
- package/dist/__tests__/TestParsing.d.ts +0 -1
- package/dist/__tests__/TestParsing.js +0 -128
- package/dist/__tests__/constants.d.ts +0 -42
- package/dist/__tests__/constants.js +0 -53
- package/examples/node-inspector-js/index.js +0 -30
- package/examples/node-inspector-js/package.json +0 -15
- package/examples/node-inspector-js/yarn.lock +0 -6
- package/examples/node-inspector-ts/dist/index.js +0 -34
- package/examples/node-inspector-ts/index.ts +0 -30
- package/examples/node-inspector-ts/package.json +0 -18
- package/examples/node-inspector-ts/tsconfig.json +0 -15
- package/examples/node-inspector-ts/yarn.lock +0 -11
- package/reason/AvoInspector.rei +0 -11
- package/reason/NodeAvoInspector.re +0 -27
- package/src/AvoDeduplicator.ts +0 -150
- package/src/AvoEncryption.ts +0 -90
- package/src/AvoGuid.ts +0 -9
- package/src/AvoInspector.ts +0 -498
- package/src/AvoInspectorEnv.ts +0 -8
- package/src/AvoNetworkCallsHandler.ts +0 -340
- package/src/AvoSchemaParser.ts +0 -115
- package/src/AvoStreamId.ts +0 -16
- package/src/__tests__/TestAvoEncryption.ts +0 -515
- package/src/__tests__/TestAvoEventSpecCache.ts +0 -242
- package/src/__tests__/TestAvoEventSpecFetcher.ts +0 -279
- package/src/__tests__/TestAvoInspector.ts +0 -376
- package/src/__tests__/TestAvoStreamId.ts +0 -72
- package/src/__tests__/TestDeduplicator.ts +0 -191
- package/src/__tests__/TestEventValidator.ts +0 -231
- package/src/__tests__/TestNetworkCallsHandler.ts +0 -181
- package/src/__tests__/TestNetworkCallsHandlerPublic.ts +0 -208
- package/src/__tests__/TestParsing.ts +0 -153
- package/src/__tests__/constants.ts +0 -63
- package/src/eventSpec/AvoEventSpecCache.ts +0 -119
- package/src/eventSpec/AvoEventSpecFetchTypes.ts +0 -47
- package/src/eventSpec/AvoEventSpecFetcher.ts +0 -186
- package/src/eventSpec/EventValidator.ts +0 -74
- package/src/index.ts +0 -2
- package/src/utils.ts +0 -49
- package/tsconfig.json +0 -13
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
import { AvoInspectorEnv, AvoInspectorEnvValueType } from "./AvoInspectorEnv";
|
|
2
|
-
import { AvoSchemaParser } from "./AvoSchemaParser";
|
|
3
|
-
import { AvoNetworkCallsHandler } from "./AvoNetworkCallsHandler";
|
|
4
|
-
import { AvoDeduplicator } from "./AvoDeduplicator";
|
|
5
|
-
import { AvoStreamId } from "./AvoStreamId";
|
|
6
|
-
import { AvoEventSpecFetcher } from "./eventSpec/AvoEventSpecFetcher";
|
|
7
|
-
import { AvoEventSpecCache } from "./eventSpec/AvoEventSpecCache";
|
|
8
|
-
import { EventValidator } from "./eventSpec/EventValidator";
|
|
9
|
-
import { EventSpecResponse } from "./eventSpec/AvoEventSpecFetchTypes";
|
|
10
|
-
|
|
11
|
-
import { isValueEmpty } from "./utils";
|
|
12
|
-
|
|
13
|
-
const libVersion = require("../package.json").version;
|
|
14
|
-
|
|
15
|
-
export class AvoInspector {
|
|
16
|
-
environment: AvoInspectorEnvValueType;
|
|
17
|
-
avoNetworkCallsHandler: AvoNetworkCallsHandler;
|
|
18
|
-
avoDeduplicator: AvoDeduplicator;
|
|
19
|
-
apiKey: string;
|
|
20
|
-
version: string;
|
|
21
|
-
private publicEncryptionKey?: string;
|
|
22
|
-
|
|
23
|
-
private eventSpecFetcher: AvoEventSpecFetcher | null = null;
|
|
24
|
-
private eventSpecCache: AvoEventSpecCache | null = null;
|
|
25
|
-
private eventValidator: EventValidator | null = null;
|
|
26
|
-
|
|
27
|
-
private static _shouldLog = false;
|
|
28
|
-
static get shouldLog() {
|
|
29
|
-
return this._shouldLog;
|
|
30
|
-
}
|
|
31
|
-
static set shouldLog(enable) {
|
|
32
|
-
this._shouldLog = enable;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
constructor(options: {
|
|
36
|
-
apiKey: string;
|
|
37
|
-
env: AvoInspectorEnvValueType;
|
|
38
|
-
version: string;
|
|
39
|
-
appName?: string;
|
|
40
|
-
publicEncryptionKey?: string;
|
|
41
|
-
}) {
|
|
42
|
-
// the constructor does aggressive null/undefined checking because same code paths will be accessible from JS
|
|
43
|
-
if (isValueEmpty(options.env)) {
|
|
44
|
-
this.environment = AvoInspectorEnv.Dev;
|
|
45
|
-
console.warn(
|
|
46
|
-
"[Avo Inspector] No environment provided. Defaulting to dev."
|
|
47
|
-
);
|
|
48
|
-
} else if (Object.values(AvoInspectorEnv).indexOf(options.env) === -1) {
|
|
49
|
-
this.environment = AvoInspectorEnv.Dev;
|
|
50
|
-
console.warn(
|
|
51
|
-
"[Avo Inspector] Unsupported environment provided. Defaulting to dev. Supported environments - Dev, Staging, Prod."
|
|
52
|
-
);
|
|
53
|
-
} else {
|
|
54
|
-
this.environment = options.env;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (isValueEmpty(options.apiKey)) {
|
|
58
|
-
throw new Error(
|
|
59
|
-
"[Avo Inspector] No API key provided. Inspector can't operate without API key."
|
|
60
|
-
);
|
|
61
|
-
} else {
|
|
62
|
-
this.apiKey = options.apiKey;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (isValueEmpty(options.version)) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
"[Avo Inspector] No version provided. Many features of Inspector rely on versioning. Please provide comparable string version, i.e. integer or semantic."
|
|
68
|
-
);
|
|
69
|
-
} else {
|
|
70
|
-
this.version = options.version;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
this.publicEncryptionKey = options.publicEncryptionKey;
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
this.publicEncryptionKey &&
|
|
77
|
-
this.environment !== AvoInspectorEnv.Prod
|
|
78
|
-
) {
|
|
79
|
-
const hexPattern = /^[0-9a-fA-F]+$/;
|
|
80
|
-
if (!hexPattern.test(this.publicEncryptionKey) || this.publicEncryptionKey.length !== 130) {
|
|
81
|
-
console.warn(
|
|
82
|
-
"[Avo Inspector] Warning: publicEncryptionKey does not look like a valid uncompressed P-256 public key (expected 130 hex characters). Encryption may fail."
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (this.environment === AvoInspectorEnv.Dev) {
|
|
88
|
-
AvoInspector._shouldLog = true;
|
|
89
|
-
} else {
|
|
90
|
-
AvoInspector._shouldLog = false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
this.avoNetworkCallsHandler = new AvoNetworkCallsHandler(
|
|
94
|
-
this.apiKey,
|
|
95
|
-
this.environment.toString(),
|
|
96
|
-
options.appName || "",
|
|
97
|
-
this.version,
|
|
98
|
-
libVersion,
|
|
99
|
-
this.publicEncryptionKey
|
|
100
|
-
);
|
|
101
|
-
this.avoDeduplicator = new AvoDeduplicator();
|
|
102
|
-
|
|
103
|
-
// Initialize event spec validation for dev/staging only
|
|
104
|
-
if (this.environment !== AvoInspectorEnv.Prod) {
|
|
105
|
-
this.eventSpecFetcher = new AvoEventSpecFetcher(this.apiKey);
|
|
106
|
-
this.eventSpecCache = new AvoEventSpecCache();
|
|
107
|
-
this.eventValidator = new EventValidator();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
trackSchemaFromEvent(
|
|
112
|
-
eventName: string,
|
|
113
|
-
eventProperties: { [propName: string]: any },
|
|
114
|
-
streamId?: string
|
|
115
|
-
): Promise<
|
|
116
|
-
Array<{
|
|
117
|
-
propertyName: string;
|
|
118
|
-
propertyType: string;
|
|
119
|
-
children?: any;
|
|
120
|
-
}>
|
|
121
|
-
> {
|
|
122
|
-
try {
|
|
123
|
-
const avoStreamId = new AvoStreamId(streamId);
|
|
124
|
-
const anonymousId = avoStreamId.streamId;
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
this.avoDeduplicator.shouldRegisterEvent(
|
|
128
|
-
eventName,
|
|
129
|
-
eventProperties,
|
|
130
|
-
false
|
|
131
|
-
)
|
|
132
|
-
) {
|
|
133
|
-
if (AvoInspector.shouldLog) {
|
|
134
|
-
console.log(
|
|
135
|
-
"Avo Inspector: supplied event " +
|
|
136
|
-
eventName +
|
|
137
|
-
" with params " +
|
|
138
|
-
JSON.stringify(eventProperties)
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
let eventSchema = this.extractSchema(eventProperties, false);
|
|
142
|
-
|
|
143
|
-
// Fire-and-forget: validate against event spec (dev/staging only)
|
|
144
|
-
this.fetchAndValidateAsync(eventName, eventSchema, anonymousId, eventProperties);
|
|
145
|
-
|
|
146
|
-
return this.trackSchemaInternal(
|
|
147
|
-
eventName,
|
|
148
|
-
eventSchema,
|
|
149
|
-
null,
|
|
150
|
-
null,
|
|
151
|
-
anonymousId,
|
|
152
|
-
eventProperties
|
|
153
|
-
).then(() => {
|
|
154
|
-
return eventSchema;
|
|
155
|
-
});
|
|
156
|
-
} else {
|
|
157
|
-
if (AvoInspector.shouldLog) {
|
|
158
|
-
console.log("Avo Inspector: Deduplicated event: " + eventName);
|
|
159
|
-
}
|
|
160
|
-
return Promise.resolve([]);
|
|
161
|
-
}
|
|
162
|
-
} catch (e) {
|
|
163
|
-
console.error(
|
|
164
|
-
"Avo Inspector: something went wrong. Please report to support@avo.app.",
|
|
165
|
-
e
|
|
166
|
-
);
|
|
167
|
-
return Promise.reject(
|
|
168
|
-
"Avo Inspector: something went wrong. Please report to support@avo.app."
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
_avoFunctionTrackSchemaFromEvent(
|
|
174
|
-
eventName: string,
|
|
175
|
-
eventProperties: { [propName: string]: any },
|
|
176
|
-
eventId: string,
|
|
177
|
-
eventHash: string,
|
|
178
|
-
streamId?: string
|
|
179
|
-
): Promise<
|
|
180
|
-
Array<{
|
|
181
|
-
propertyName: string;
|
|
182
|
-
propertyType: string;
|
|
183
|
-
children?: any;
|
|
184
|
-
}>
|
|
185
|
-
> {
|
|
186
|
-
try {
|
|
187
|
-
if (
|
|
188
|
-
this.avoDeduplicator.shouldRegisterEvent(
|
|
189
|
-
eventName,
|
|
190
|
-
eventProperties,
|
|
191
|
-
true
|
|
192
|
-
)
|
|
193
|
-
) {
|
|
194
|
-
if (AvoInspector.shouldLog) {
|
|
195
|
-
console.log(
|
|
196
|
-
"Avo Inspector: supplied event " +
|
|
197
|
-
eventName +
|
|
198
|
-
" with params " +
|
|
199
|
-
JSON.stringify(eventProperties)
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
let eventSchema = this.extractSchema(eventProperties, false);
|
|
203
|
-
const avoStreamId = new AvoStreamId(streamId);
|
|
204
|
-
|
|
205
|
-
// Fire-and-forget: validate against event spec (dev/staging only)
|
|
206
|
-
this.fetchAndValidateAsync(eventName, eventSchema, avoStreamId.streamId, eventProperties);
|
|
207
|
-
|
|
208
|
-
return this.trackSchemaInternal(
|
|
209
|
-
eventName,
|
|
210
|
-
eventSchema,
|
|
211
|
-
eventId,
|
|
212
|
-
eventHash,
|
|
213
|
-
avoStreamId.streamId,
|
|
214
|
-
eventProperties
|
|
215
|
-
).then(() => {
|
|
216
|
-
return eventSchema;
|
|
217
|
-
});
|
|
218
|
-
} else {
|
|
219
|
-
if (AvoInspector.shouldLog) {
|
|
220
|
-
console.log("Avo Inspector: Deduplicated event: " + eventName);
|
|
221
|
-
}
|
|
222
|
-
return Promise.resolve([]);
|
|
223
|
-
}
|
|
224
|
-
} catch (e) {
|
|
225
|
-
console.error(
|
|
226
|
-
"Avo Inspector: something went wrong. Please report to support@avo.app.",
|
|
227
|
-
e
|
|
228
|
-
);
|
|
229
|
-
return Promise.reject(
|
|
230
|
-
"Avo Inspector: something went wrong. Please report to support@avo.app."
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
private async trackSchemaInternal(
|
|
236
|
-
eventName: string,
|
|
237
|
-
eventSchema: Array<{
|
|
238
|
-
propertyName: string;
|
|
239
|
-
propertyType: string;
|
|
240
|
-
children?: any;
|
|
241
|
-
}>,
|
|
242
|
-
eventId: string | null,
|
|
243
|
-
eventHash: string | null,
|
|
244
|
-
anonymousId: string,
|
|
245
|
-
rawEventProperties?: { [propName: string]: any }
|
|
246
|
-
): Promise<void> {
|
|
247
|
-
try {
|
|
248
|
-
await this.avoNetworkCallsHandler.callInspectorWithBatchBody([
|
|
249
|
-
this.avoNetworkCallsHandler.bodyForEventSchemaCall(
|
|
250
|
-
anonymousId,
|
|
251
|
-
eventName,
|
|
252
|
-
eventSchema,
|
|
253
|
-
eventId,
|
|
254
|
-
eventHash,
|
|
255
|
-
rawEventProperties
|
|
256
|
-
)]);
|
|
257
|
-
if (AvoInspector.shouldLog) {
|
|
258
|
-
console.log("Avo Inspector: schema sent successfully.");
|
|
259
|
-
}
|
|
260
|
-
} catch (err) {
|
|
261
|
-
console.error("Avo Inspector: schema sending failed: " + err + ".");
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
enableLogging(enable: boolean) {
|
|
266
|
-
AvoInspector._shouldLog = enable;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
extractSchema(
|
|
270
|
-
eventProperties: {
|
|
271
|
-
[propName: string]: any;
|
|
272
|
-
},
|
|
273
|
-
shouldLogIfEnabled = true
|
|
274
|
-
): Array<{
|
|
275
|
-
propertyName: string;
|
|
276
|
-
propertyType: string;
|
|
277
|
-
children?: any;
|
|
278
|
-
}> {
|
|
279
|
-
try {
|
|
280
|
-
if (this.avoDeduplicator.hasSeenEventParams(eventProperties, true)) {
|
|
281
|
-
if (shouldLogIfEnabled && AvoInspector.shouldLog) {
|
|
282
|
-
console.warn(
|
|
283
|
-
"Avo Inspector: WARNING! You are trying to extract schema shape that was just reported by your Codegen. " +
|
|
284
|
-
"This is an indicator of duplicate inspector reporting. " +
|
|
285
|
-
"Please reach out to support@avo.app for advice if you are not sure how to handle this."
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (AvoInspector.shouldLog) {
|
|
291
|
-
console.log(
|
|
292
|
-
"Avo Inspector: extracting schema from " +
|
|
293
|
-
JSON.stringify(eventProperties)
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return AvoSchemaParser.extractSchema(eventProperties);
|
|
298
|
-
} catch (e) {
|
|
299
|
-
console.error(
|
|
300
|
-
"Avo Inspector: something went wrong. Please report to support@avo.app.",
|
|
301
|
-
e
|
|
302
|
-
);
|
|
303
|
-
return [];
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
private fetchAndValidateAsync(
|
|
308
|
-
eventName: string,
|
|
309
|
-
eventSchema: Array<{
|
|
310
|
-
propertyName: string;
|
|
311
|
-
propertyType: string;
|
|
312
|
-
children?: any;
|
|
313
|
-
}>,
|
|
314
|
-
anonymousId: string,
|
|
315
|
-
rawEventProperties?: { [propName: string]: any }
|
|
316
|
-
): void {
|
|
317
|
-
if (!this.eventSpecFetcher || !this.eventSpecCache || !this.eventValidator) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const cacheKey = AvoEventSpecCache.makeKey(this.apiKey, anonymousId, eventName);
|
|
322
|
-
const fetcher = this.eventSpecFetcher;
|
|
323
|
-
const cache = this.eventSpecCache;
|
|
324
|
-
const validator = this.eventValidator;
|
|
325
|
-
const networkHandler = this.avoNetworkCallsHandler;
|
|
326
|
-
|
|
327
|
-
const validateAndSend = (specResponse: EventSpecResponse) => {
|
|
328
|
-
if (specResponse.eventSpec === null) {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const eventProperties = eventSchema.map((prop) => ({
|
|
333
|
-
propertyName: prop.propertyName,
|
|
334
|
-
propertyType: prop.propertyType,
|
|
335
|
-
...(rawEventProperties && rawEventProperties[prop.propertyName] !== undefined
|
|
336
|
-
? { propertyValue: String(rawEventProperties[prop.propertyName]) }
|
|
337
|
-
: {}),
|
|
338
|
-
}));
|
|
339
|
-
|
|
340
|
-
const results = validator.validate(
|
|
341
|
-
specResponse.eventSpec,
|
|
342
|
-
eventProperties,
|
|
343
|
-
eventName
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
networkHandler.callInspectorWithBatchBody([
|
|
347
|
-
networkHandler.bodyForValidatedEventSchemaCall(
|
|
348
|
-
anonymousId,
|
|
349
|
-
eventName,
|
|
350
|
-
specResponse.metadata,
|
|
351
|
-
results
|
|
352
|
-
),
|
|
353
|
-
]).catch(() => {
|
|
354
|
-
// Fire-and-forget: validation send failures are non-critical
|
|
355
|
-
});
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
const cached = cache.get(cacheKey);
|
|
359
|
-
if (cached !== undefined) {
|
|
360
|
-
validateAndSend(cached);
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
fetcher.fetch(eventName, anonymousId, (result) => {
|
|
365
|
-
if (result !== null) {
|
|
366
|
-
cache.set(cacheKey, result);
|
|
367
|
-
validateAndSend(result);
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
destroy(): void {
|
|
373
|
-
if (this.eventSpecFetcher) {
|
|
374
|
-
this.eventSpecFetcher.destroy();
|
|
375
|
-
this.eventSpecFetcher = null;
|
|
376
|
-
}
|
|
377
|
-
if (this.eventSpecCache) {
|
|
378
|
-
this.eventSpecCache.flush();
|
|
379
|
-
this.eventSpecCache = null;
|
|
380
|
-
}
|
|
381
|
-
this.eventValidator = null;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import { AvoGuid } from "./AvoGuid";
|
|
2
|
-
import { AvoInspector } from "./AvoInspector";
|
|
3
|
-
import { AvoEncryption } from "./AvoEncryption";
|
|
4
|
-
import { request } from "https";
|
|
5
|
-
import { EventSpecMetadata, PropertyValidationResult } from "./eventSpec/AvoEventSpecFetchTypes";
|
|
6
|
-
|
|
7
|
-
export interface BaseBody {
|
|
8
|
-
apiKey: string;
|
|
9
|
-
appName: string;
|
|
10
|
-
appVersion: string;
|
|
11
|
-
libVersion: string;
|
|
12
|
-
env: string;
|
|
13
|
-
libPlatform: "node";
|
|
14
|
-
messageId: string;
|
|
15
|
-
anonymousId: string;
|
|
16
|
-
createdAt: string;
|
|
17
|
-
samplingRate: number;
|
|
18
|
-
publicEncryptionKey?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface EventPropertyEncrypted {
|
|
22
|
-
propertyName: string;
|
|
23
|
-
propertyType: string;
|
|
24
|
-
encryptedPropertyValue: string;
|
|
25
|
-
children?: any;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface EventPropertyPlain {
|
|
29
|
-
propertyName: string;
|
|
30
|
-
propertyType: string;
|
|
31
|
-
children?: any;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type EventProperty = EventPropertyEncrypted | EventPropertyPlain;
|
|
35
|
-
|
|
36
|
-
export interface EventSchemaBody extends BaseBody {
|
|
37
|
-
type: "event";
|
|
38
|
-
eventName: string;
|
|
39
|
-
eventProperties: Array<EventProperty>;
|
|
40
|
-
avoFunction: boolean;
|
|
41
|
-
eventId: string | null;
|
|
42
|
-
eventHash: string | null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface ValidatedEventSchemaBody extends BaseBody {
|
|
46
|
-
type: "validatedEvent";
|
|
47
|
-
eventName: string;
|
|
48
|
-
eventSpecMetadata: EventSpecMetadata;
|
|
49
|
-
propertyResults: PropertyValidationResult[];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type InspectorBody = EventSchemaBody | ValidatedEventSchemaBody;
|
|
53
|
-
|
|
54
|
-
export class AvoNetworkCallsHandler {
|
|
55
|
-
private apiKey: string;
|
|
56
|
-
private envName: string;
|
|
57
|
-
private appName: string;
|
|
58
|
-
private appVersion: string;
|
|
59
|
-
private libVersion: string;
|
|
60
|
-
private samplingRate: number = 1.0;
|
|
61
|
-
private publicEncryptionKey?: string;
|
|
62
|
-
|
|
63
|
-
private static trackingEndpoint = "/inspector/v1/track";
|
|
64
|
-
|
|
65
|
-
constructor(
|
|
66
|
-
apiKey: string,
|
|
67
|
-
envName: string,
|
|
68
|
-
appName: string,
|
|
69
|
-
appVersion: string,
|
|
70
|
-
libVersion: string,
|
|
71
|
-
publicEncryptionKey?: string
|
|
72
|
-
) {
|
|
73
|
-
this.apiKey = apiKey;
|
|
74
|
-
this.envName = envName;
|
|
75
|
-
this.appName = appName;
|
|
76
|
-
this.appVersion = appVersion;
|
|
77
|
-
this.libVersion = libVersion;
|
|
78
|
-
this.publicEncryptionKey = publicEncryptionKey;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
callInspectorWithBatchBody(
|
|
82
|
-
inEvents: Array<InspectorBody>
|
|
83
|
-
): Promise<void> {
|
|
84
|
-
const events = inEvents.filter((x) => x != null);
|
|
85
|
-
|
|
86
|
-
if (events.length === 0) {
|
|
87
|
-
return Promise.resolve();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (Math.random() > this.samplingRate) {
|
|
91
|
-
if (AvoInspector.shouldLog) {
|
|
92
|
-
console.log(
|
|
93
|
-
"Avo Inspector: last event schema dropped due to sampling rate."
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
return Promise.resolve();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (AvoInspector.shouldLog) {
|
|
100
|
-
events.forEach(function (event) {
|
|
101
|
-
if (event.type === "event") {
|
|
102
|
-
let schemaEvent: EventSchemaBody = event;
|
|
103
|
-
console.log(
|
|
104
|
-
"Avo Inspector: sending event " +
|
|
105
|
-
schemaEvent.eventName +
|
|
106
|
-
" with schema " +
|
|
107
|
-
JSON.stringify(schemaEvent.eventProperties)
|
|
108
|
-
);
|
|
109
|
-
} else if (event.type === "validatedEvent") {
|
|
110
|
-
let validatedEvent: ValidatedEventSchemaBody = event;
|
|
111
|
-
console.log(
|
|
112
|
-
"Avo Inspector: sending validated event " +
|
|
113
|
-
validatedEvent.eventName +
|
|
114
|
-
" with " +
|
|
115
|
-
validatedEvent.propertyResults.length +
|
|
116
|
-
" property results"
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return new Promise((resolve, reject) => {
|
|
123
|
-
const data = JSON.stringify(events);
|
|
124
|
-
const options = {
|
|
125
|
-
hostname: "api.avo.app",
|
|
126
|
-
port: 443,
|
|
127
|
-
path: AvoNetworkCallsHandler.trackingEndpoint,
|
|
128
|
-
method: "POST",
|
|
129
|
-
headers: {
|
|
130
|
-
"Content-Type": "text/plain",
|
|
131
|
-
"Content-Length": Buffer.byteLength(data),
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
const req = request(options, (res: any) => {
|
|
135
|
-
const chunks: any = [];
|
|
136
|
-
res.on("data", (data: any) => chunks.push(data));
|
|
137
|
-
res.on("end", () => {
|
|
138
|
-
try {
|
|
139
|
-
const data = JSON.parse(Buffer.concat(chunks).toString());
|
|
140
|
-
this.samplingRate = data.samplingRate;
|
|
141
|
-
} catch (e) {}
|
|
142
|
-
resolve();
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
req.write(data);
|
|
146
|
-
req.setTimeout(10_000);
|
|
147
|
-
req.on("error", () => {
|
|
148
|
-
reject("Request failed");
|
|
149
|
-
});
|
|
150
|
-
req.on("timeout", () => {
|
|
151
|
-
req.destroy();
|
|
152
|
-
reject("Request timed out");
|
|
153
|
-
});
|
|
154
|
-
req.end();
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
bodyForEventSchemaCall(
|
|
159
|
-
anonymousId: string,
|
|
160
|
-
eventName: string,
|
|
161
|
-
eventProperties: Array<{
|
|
162
|
-
propertyName: string;
|
|
163
|
-
propertyType: string;
|
|
164
|
-
children?: any;
|
|
165
|
-
}>,
|
|
166
|
-
eventId: string | null,
|
|
167
|
-
eventHash: string | null,
|
|
168
|
-
rawEventProperties?: { [propName: string]: any }
|
|
169
|
-
): EventSchemaBody {
|
|
170
|
-
let eventSchemaBody = this.createBaseCallBody(anonymousId) as EventSchemaBody;
|
|
171
|
-
eventSchemaBody.type = "event";
|
|
172
|
-
eventSchemaBody.eventName = eventName;
|
|
173
|
-
|
|
174
|
-
if (AvoEncryption.shouldEncrypt(this.envName, this.publicEncryptionKey) && rawEventProperties) {
|
|
175
|
-
eventSchemaBody.eventProperties = this.encryptProperties(eventProperties, rawEventProperties);
|
|
176
|
-
} else {
|
|
177
|
-
eventSchemaBody.eventProperties = eventProperties;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (eventId != null) {
|
|
181
|
-
eventSchemaBody.avoFunction = true;
|
|
182
|
-
eventSchemaBody.eventId = eventId;
|
|
183
|
-
eventSchemaBody.eventHash = eventHash;
|
|
184
|
-
} else {
|
|
185
|
-
eventSchemaBody.avoFunction = false;
|
|
186
|
-
eventSchemaBody.eventId = null;
|
|
187
|
-
eventSchemaBody.eventHash = null;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return eventSchemaBody;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
bodyForValidatedEventSchemaCall(
|
|
194
|
-
anonymousId: string,
|
|
195
|
-
eventName: string,
|
|
196
|
-
eventSpecMetadata: EventSpecMetadata,
|
|
197
|
-
propertyResults: PropertyValidationResult[]
|
|
198
|
-
): ValidatedEventSchemaBody {
|
|
199
|
-
let body = this.createBaseCallBody(anonymousId) as ValidatedEventSchemaBody;
|
|
200
|
-
body.type = "validatedEvent";
|
|
201
|
-
body.eventName = eventName;
|
|
202
|
-
body.eventSpecMetadata = eventSpecMetadata;
|
|
203
|
-
body.propertyResults = propertyResults;
|
|
204
|
-
return body;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
private encryptProperties(
|
|
208
|
-
properties: Array<{
|
|
209
|
-
propertyName: string;
|
|
210
|
-
propertyType: string;
|
|
211
|
-
children?: any;
|
|
212
|
-
}>,
|
|
213
|
-
rawEventProperties: { [propName: string]: any }
|
|
214
|
-
): Array<EventProperty> {
|
|
215
|
-
const result: Array<EventProperty> = [];
|
|
216
|
-
|
|
217
|
-
for (const prop of properties) {
|
|
218
|
-
// List-type properties: omit entirely
|
|
219
|
-
if (AvoEncryption.isListType(prop.propertyType)) {
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const rawValue = rawEventProperties[prop.propertyName];
|
|
224
|
-
const jsonValue = JSON.stringify(rawValue) ?? "null";
|
|
225
|
-
|
|
226
|
-
const encrypted = AvoEncryption.encryptValue(
|
|
227
|
-
jsonValue,
|
|
228
|
-
this.publicEncryptionKey!
|
|
229
|
-
);
|
|
230
|
-
|
|
231
|
-
if (encrypted === null) {
|
|
232
|
-
// Encryption failure: omit the property (warning already logged by encryptValue)
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
result.push({
|
|
237
|
-
propertyName: prop.propertyName,
|
|
238
|
-
propertyType: prop.propertyType,
|
|
239
|
-
encryptedPropertyValue: encrypted,
|
|
240
|
-
...(prop.children !== undefined ? { children: prop.children } : {}),
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return result;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private createBaseCallBody(anonymousId: string): BaseBody {
|
|
248
|
-
const body: BaseBody = {
|
|
249
|
-
apiKey: this.apiKey,
|
|
250
|
-
appName: this.appName,
|
|
251
|
-
appVersion: this.appVersion,
|
|
252
|
-
libVersion: this.libVersion,
|
|
253
|
-
env: this.envName,
|
|
254
|
-
libPlatform: "node",
|
|
255
|
-
messageId: AvoGuid.newGuid(),
|
|
256
|
-
anonymousId: anonymousId,
|
|
257
|
-
createdAt: new Date().toISOString(),
|
|
258
|
-
samplingRate: this.samplingRate,
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
if (this.publicEncryptionKey && this.publicEncryptionKey.length > 0) {
|
|
262
|
-
body.publicEncryptionKey = this.publicEncryptionKey;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return body;
|
|
266
|
-
}
|
|
267
|
-
}
|