avo-inspector 3.1.0-alpha.2 → 3.2.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/README.md +6 -13
- package/dist/AvoBatcher.js +52 -44
- package/dist/AvoDeduplicator.js +46 -45
- package/dist/AvoEncryption.js +147 -253
- package/dist/AvoInspector.js +295 -334
- package/dist/AvoNetworkCallsHandler.d.ts +6 -0
- package/dist/AvoNetworkCallsHandler.js +180 -136
- package/dist/AvoSchemaParser.js +34 -71
- package/dist/AvoStreamId.js +67 -129
- package/dist/__mocks__/xhr.js +14 -0
- package/dist/__mocks__/xhrError.js +14 -0
- package/dist/__tests__/AvoEncryption.d.ts +1 -0
- package/dist/__tests__/AvoEncryption.js +272 -0
- package/dist/__tests__/AvoEncryptionIntegration.d.ts +1 -0
- package/dist/__tests__/AvoEncryptionIntegration.js +191 -0
- package/dist/__tests__/AvoEventSpecFetcher.d.ts +1 -0
- package/dist/__tests__/AvoEventSpecFetcher.js +377 -0
- package/dist/__tests__/AvoInspector.d.ts +1 -0
- package/dist/__tests__/AvoInspector.js +335 -0
- package/dist/__tests__/Batcher.d.ts +1 -0
- package/dist/__tests__/Batcher.js +214 -0
- package/dist/__tests__/BatcherPublic.d.ts +1 -0
- package/dist/__tests__/BatcherPublic.js +139 -0
- package/dist/__tests__/Deduplicator.d.ts +1 -0
- package/dist/__tests__/Deduplicator.js +190 -0
- package/dist/__tests__/EventSpecCache.d.ts +1 -0
- package/dist/__tests__/EventSpecCache.js +143 -0
- package/dist/__tests__/EventValidator.d.ts +1 -0
- package/dist/__tests__/EventValidator.js +543 -0
- package/dist/__tests__/InstallationId.d.ts +1 -0
- package/dist/__tests__/InstallationId.js +60 -0
- package/dist/__tests__/NetworkCallsHandler.d.ts +1 -0
- package/dist/__tests__/NetworkCallsHandler.js +222 -0
- package/dist/__tests__/NetworkCallsHandlerGzip_test.d.ts +1 -0
- package/dist/__tests__/NetworkCallsHandlerModelA.d.ts +1 -0
- package/dist/__tests__/NetworkCallsHandlerModelA.js +206 -0
- package/dist/__tests__/Parsing.d.ts +1 -0
- package/dist/__tests__/Parsing.js +115 -0
- package/dist/__tests__/Sessions.d.ts +1 -0
- package/dist/__tests__/Sessions.js +10 -0
- package/dist/__tests__/Storage.d.ts +1 -0
- package/dist/__tests__/Storage.js +173 -0
- package/dist/__tests__/StreamId.d.ts +1 -0
- package/dist/__tests__/StreamId.js +153 -0
- package/dist/__tests__/constants.js +48 -0
- package/dist/eventSpec/AvoEventSpecCache.js +94 -82
- package/dist/eventSpec/AvoEventSpecFetcher.js +200 -117
- package/dist/eventSpec/EventValidator.js +460 -304
- package/dist/index.js +1 -1
- package/dist/lite/AvoNetworkCallsHandlerLite.d.ts +6 -0
- package/dist/lite/AvoNetworkCallsHandlerLite.js +110 -1
- package/package.json +2 -2
package/dist/AvoInspector.js
CHANGED
|
@@ -1,51 +1,64 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
2
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
39
|
exports.AvoInspector = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (newSize < 1) {
|
|
20
|
-
this._batchSize = 1;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
this._batchSize = newSize;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
static get batchFlushSeconds() {
|
|
27
|
-
return this._batchFlushSeconds;
|
|
28
|
-
}
|
|
29
|
-
static get shouldLog() {
|
|
30
|
-
return this._shouldLog;
|
|
31
|
-
}
|
|
32
|
-
static set shouldLog(enable) {
|
|
33
|
-
this._shouldLog = enable;
|
|
34
|
-
}
|
|
35
|
-
static get networkTimeout() {
|
|
36
|
-
return this._networkTimeout;
|
|
37
|
-
}
|
|
38
|
-
static set networkTimeout(timeout) {
|
|
39
|
-
this._networkTimeout = timeout;
|
|
40
|
-
}
|
|
41
|
-
constructor(options) {
|
|
40
|
+
var AvoInspectorEnv_1 = require("./AvoInspectorEnv");
|
|
41
|
+
var AvoSchemaParser_1 = require("./AvoSchemaParser");
|
|
42
|
+
var AvoBatcher_1 = require("./AvoBatcher");
|
|
43
|
+
var AvoNetworkCallsHandler_1 = require("./AvoNetworkCallsHandler");
|
|
44
|
+
var AvoStorage_1 = require("./AvoStorage");
|
|
45
|
+
var AvoDeduplicator_1 = require("./AvoDeduplicator");
|
|
46
|
+
var AvoStreamId_1 = require("./AvoStreamId");
|
|
47
|
+
var AvoEventSpecCache_1 = require("./eventSpec/AvoEventSpecCache");
|
|
48
|
+
var AvoEventSpecFetcher_1 = require("./eventSpec/AvoEventSpecFetcher");
|
|
49
|
+
var EventValidator_1 = require("./eventSpec/EventValidator");
|
|
50
|
+
var utils_1 = require("./utils");
|
|
51
|
+
var libVersion = require("../package.json").version;
|
|
52
|
+
var AvoInspector = /** @class */ (function () {
|
|
53
|
+
function AvoInspector(options) {
|
|
54
|
+
var _this = this;
|
|
42
55
|
this.currentBranchId = null;
|
|
43
56
|
// the constructor does aggressive null/undefined checking because same code paths will be accessible from JS
|
|
44
57
|
if ((0, utils_1.isValueEmpty)(options.env)) {
|
|
45
58
|
this.environment = AvoInspectorEnv_1.AvoInspectorEnv.Dev;
|
|
46
59
|
console.warn("[Avo Inspector] No environment provided. Defaulting to dev.");
|
|
47
60
|
}
|
|
48
|
-
else if (
|
|
61
|
+
else if (Object.values(AvoInspectorEnv_1.AvoInspectorEnv).indexOf(options.env) === -1) {
|
|
49
62
|
this.environment = AvoInspectorEnv_1.AvoInspectorEnv.Dev;
|
|
50
63
|
console.warn("[Avo Inspector] Unsupported environment provided. Defaulting to dev. Supported environments - Dev, Staging, Prod.");
|
|
51
64
|
}
|
|
@@ -64,6 +77,9 @@ class AvoInspector {
|
|
|
64
77
|
else {
|
|
65
78
|
this.version = options.version;
|
|
66
79
|
}
|
|
80
|
+
if (options.publicEncryptionKey) {
|
|
81
|
+
this.publicEncryptionKey = options.publicEncryptionKey;
|
|
82
|
+
}
|
|
67
83
|
if (this.environment === AvoInspectorEnv_1.AvoInspectorEnv.Dev) {
|
|
68
84
|
AvoInspector._batchSize = 1;
|
|
69
85
|
AvoInspector._shouldLog = true;
|
|
@@ -73,147 +89,161 @@ class AvoInspector {
|
|
|
73
89
|
AvoInspector._batchFlushSeconds = 30;
|
|
74
90
|
AvoInspector._shouldLog = false;
|
|
75
91
|
}
|
|
76
|
-
AvoInspector.avoStorage = new AvoStorage_1.AvoStorage(AvoInspector._shouldLog, options.suffix != null ? options.suffix : "");
|
|
77
|
-
this.publicEncryptionKey = options.publicEncryptionKey;
|
|
78
|
-
this.avoNetworkCallsHandler = new AvoNetworkCallsHandler_1.AvoNetworkCallsHandler(this.apiKey, this.environment.toString(), options.appName || "", this.version, libVersion, this.publicEncryptionKey);
|
|
79
|
-
this.avoBatcher = new AvoBatcher_1.AvoBatcher(this.avoNetworkCallsHandler);
|
|
80
|
-
this.avoDeduplicator = new AvoDeduplicator_1.AvoDeduplicator();
|
|
81
|
-
this.streamId = AvoStreamId_1.AvoStreamId.streamId;
|
|
82
|
-
// Enable event spec fetching if streamId is present (note: "unknown" is truthy and will trigger initEventSpecModules; see streamId edge case in spec)
|
|
83
|
-
if (this.streamId && this.environment !== AvoInspectorEnv_1.AvoInspectorEnv.Prod) {
|
|
84
|
-
this._eventSpecReady = this.initEventSpecModules();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
async initEventSpecModules() {
|
|
88
|
-
// ORDERING CONSTRAINT: All instance field assignments (this.eventSpecCache,
|
|
89
|
-
// this.eventSpecFetcher, this._validateEvent) MUST occur inside the try block,
|
|
90
|
-
// before the finally block clears this._eventSpecReady. The finally block sets
|
|
91
|
-
// this._eventSpecReady = undefined to signal completion. Concurrent callers that
|
|
92
|
-
// await this._eventSpecReady (via ensureEventSpecReady) will resume after the
|
|
93
|
-
// finally block executes. If any field assignment were moved to or after the
|
|
94
|
-
// finally block, those concurrent callers would find fields unpopulated, creating
|
|
95
|
-
// a race condition where they incorrectly proceed as if no event spec is available.
|
|
96
92
|
try {
|
|
97
|
-
|
|
98
|
-
Promise.resolve().then(() => require("./eventSpec/AvoEventSpecCache")),
|
|
99
|
-
Promise.resolve().then(() => require("./eventSpec/AvoEventSpecFetcher")),
|
|
100
|
-
Promise.resolve().then(() => require("./eventSpec/EventValidator"))
|
|
101
|
-
]);
|
|
102
|
-
this.eventSpecCache = new EventSpecCache(AvoInspector._shouldLog);
|
|
103
|
-
this.eventSpecFetcher = new AvoEventSpecFetcher(AvoInspector._networkTimeout, AvoInspector._shouldLog, this.environment);
|
|
104
|
-
this._validateEvent = validateEvent;
|
|
105
|
-
if (AvoInspector._shouldLog) {
|
|
106
|
-
console.log("[Avo Inspector] Event spec fetching and validation enabled");
|
|
107
|
-
if (this.publicEncryptionKey) {
|
|
108
|
-
console.log("[Avo Inspector] Property value encryption enabled");
|
|
109
|
-
}
|
|
110
|
-
}
|
|
93
|
+
AvoInspector.avoStorage = new AvoStorage_1.AvoStorage(AvoInspector._shouldLog);
|
|
111
94
|
}
|
|
112
|
-
catch (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
finally {
|
|
118
|
-
this._eventSpecReady = undefined;
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.error("[Avo Inspector] Failed to initialize storage. " +
|
|
97
|
+
"If you are using React Native, make sure @react-native-async-storage/async-storage is installed.", e);
|
|
119
98
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
fetcher: this.eventSpecFetcher,
|
|
131
|
-
validate: this._validateEvent,
|
|
132
|
-
streamId: this.streamId,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
async trackSchemaFromEvent(eventName, eventProperties) {
|
|
136
|
-
try {
|
|
137
|
-
if (this.avoDeduplicator.shouldRegisterEvent(eventName, eventProperties, false)) {
|
|
138
|
-
if (AvoInspector.shouldLog) {
|
|
139
|
-
console.log("Avo Inspector: supplied event " +
|
|
140
|
-
eventName +
|
|
141
|
-
" with params " +
|
|
142
|
-
JSON.stringify(eventProperties));
|
|
143
|
-
}
|
|
144
|
-
const eventSchema = await this.extractSchema(eventProperties, false);
|
|
145
|
-
// Fetch and validate event spec (sync blocking)
|
|
146
|
-
const validationResult = await this.fetchAndValidateEvent(eventName, eventProperties);
|
|
147
|
-
if (validationResult) {
|
|
148
|
-
// Spec available: merge validation results into schema and send immediately
|
|
149
|
-
const schemaWithValidation = this.mergeValidationResults(eventSchema, validationResult);
|
|
150
|
-
this.sendEventWithValidation(eventName, schemaWithValidation, null, null, validationResult);
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
// No spec: fall back to batched flow
|
|
154
|
-
this.trackSchemaInternal(eventName, eventSchema, null, null);
|
|
99
|
+
var avoNetworkCallsHandler = new AvoNetworkCallsHandler_1.AvoNetworkCallsHandler(this.apiKey, this.environment.toString(), options.appName || "", this.version, libVersion, this.publicEncryptionKey);
|
|
100
|
+
this.avoBatcher = new AvoBatcher_1.AvoBatcher(avoNetworkCallsHandler);
|
|
101
|
+
this.avoDeduplicator = new AvoDeduplicator_1.AvoDeduplicator();
|
|
102
|
+
// Initialize event spec validation infrastructure for dev/staging
|
|
103
|
+
if (this.environment !== AvoInspectorEnv_1.AvoInspectorEnv.Prod) {
|
|
104
|
+
try {
|
|
105
|
+
this.eventSpecCache = new AvoEventSpecCache_1.EventSpecCache(AvoInspector._shouldLog);
|
|
106
|
+
this.eventSpecFetcher = new AvoEventSpecFetcher_1.AvoEventSpecFetcher(5000, AvoInspector._shouldLog, this.environment);
|
|
107
|
+
if (AvoInspector._shouldLog) {
|
|
108
|
+
console.log("[Avo Inspector] Event spec fetching and validation enabled");
|
|
155
109
|
}
|
|
156
|
-
return eventSchema;
|
|
157
110
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
console.log("Avo Inspector: Deduplicated event: " + eventName);
|
|
161
|
-
}
|
|
162
|
-
return [];
|
|
111
|
+
catch (e) {
|
|
112
|
+
console.error("[Avo Inspector] Failed to initialize event spec validation.", e);
|
|
163
113
|
}
|
|
164
114
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
115
|
+
// Fire-and-forget: eagerly initialize the anonymous ID and capture streamId
|
|
116
|
+
AvoStreamId_1.AvoStreamId.initialize().then(function (id) {
|
|
117
|
+
_this.streamId = id;
|
|
118
|
+
}).catch(function (e) {
|
|
119
|
+
console.error("Avo Inspector: failed to initialize anonymous ID.", e);
|
|
120
|
+
});
|
|
169
121
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
JSON.stringify(eventProperties));
|
|
178
|
-
}
|
|
179
|
-
const eventSchema = await this.extractSchema(eventProperties, false);
|
|
180
|
-
// Fetch and validate event spec (sync blocking)
|
|
181
|
-
const validationResult = await this.fetchAndValidateEvent(eventName, eventProperties);
|
|
182
|
-
if (validationResult) {
|
|
183
|
-
// Spec available: merge validation results into schema and send immediately
|
|
184
|
-
const schemaWithValidation = this.mergeValidationResults(eventSchema, validationResult);
|
|
185
|
-
this.sendEventWithValidation(eventName, schemaWithValidation, eventId, eventHash, validationResult);
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
// No spec: fall back to batched flow
|
|
189
|
-
this.trackSchemaInternal(eventName, eventSchema, eventId, eventHash);
|
|
190
|
-
}
|
|
191
|
-
return eventSchema;
|
|
122
|
+
Object.defineProperty(AvoInspector, "batchSize", {
|
|
123
|
+
get: function () {
|
|
124
|
+
return this._batchSize;
|
|
125
|
+
},
|
|
126
|
+
set: function (newSize) {
|
|
127
|
+
if (newSize < 1) {
|
|
128
|
+
this._batchSize = 1;
|
|
192
129
|
}
|
|
193
130
|
else {
|
|
194
|
-
|
|
195
|
-
console.log("Avo Inspector: Deduplicated event: " + eventName);
|
|
196
|
-
}
|
|
197
|
-
return [];
|
|
131
|
+
this._batchSize = newSize;
|
|
198
132
|
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
133
|
+
},
|
|
134
|
+
enumerable: false,
|
|
135
|
+
configurable: true
|
|
136
|
+
});
|
|
137
|
+
Object.defineProperty(AvoInspector, "batchFlushSeconds", {
|
|
138
|
+
get: function () {
|
|
139
|
+
return this._batchFlushSeconds;
|
|
140
|
+
},
|
|
141
|
+
enumerable: false,
|
|
142
|
+
configurable: true
|
|
143
|
+
});
|
|
144
|
+
Object.defineProperty(AvoInspector, "shouldLog", {
|
|
145
|
+
get: function () {
|
|
146
|
+
return this._shouldLog;
|
|
147
|
+
},
|
|
148
|
+
set: function (enable) {
|
|
149
|
+
this._shouldLog = enable;
|
|
150
|
+
},
|
|
151
|
+
enumerable: false,
|
|
152
|
+
configurable: true
|
|
153
|
+
});
|
|
154
|
+
AvoInspector.prototype.trackSchemaFromEvent = function (eventName, eventProperties) {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
156
|
+
var eventSchema, validationResult, schemaWithValidation, e_1;
|
|
157
|
+
return __generator(this, function (_a) {
|
|
158
|
+
switch (_a.label) {
|
|
159
|
+
case 0:
|
|
160
|
+
_a.trys.push([0, 4, , 5]);
|
|
161
|
+
if (!this.avoDeduplicator.shouldRegisterEvent(eventName, eventProperties, false)) return [3 /*break*/, 2];
|
|
162
|
+
if (AvoInspector.shouldLog) {
|
|
163
|
+
console.log("Avo Inspector: supplied event " +
|
|
164
|
+
eventName +
|
|
165
|
+
" with params " +
|
|
166
|
+
JSON.stringify(eventProperties));
|
|
167
|
+
}
|
|
168
|
+
eventSchema = this.extractSchema(eventProperties, false);
|
|
169
|
+
return [4 /*yield*/, this.fetchAndValidateEvent(eventName, eventProperties)];
|
|
170
|
+
case 1:
|
|
171
|
+
validationResult = _a.sent();
|
|
172
|
+
if (validationResult) {
|
|
173
|
+
schemaWithValidation = this.mergeValidationResults(eventSchema, validationResult);
|
|
174
|
+
// Send validated events immediately, bypassing the batcher
|
|
175
|
+
this.sendEventWithValidation(eventName, schemaWithValidation, null, null, validationResult, eventProperties);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.trackSchemaInternal(eventName, eventSchema, null, null, eventProperties);
|
|
179
|
+
}
|
|
180
|
+
return [2 /*return*/, eventSchema];
|
|
181
|
+
case 2:
|
|
182
|
+
if (AvoInspector.shouldLog) {
|
|
183
|
+
console.log("Avo Inspector: Deduplicated event: " + eventName);
|
|
184
|
+
}
|
|
185
|
+
return [2 /*return*/, []];
|
|
186
|
+
case 3: return [3 /*break*/, 5];
|
|
187
|
+
case 4:
|
|
188
|
+
e_1 = _a.sent();
|
|
189
|
+
console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e_1);
|
|
190
|
+
return [2 /*return*/, []];
|
|
191
|
+
case 5: return [2 /*return*/];
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
AvoInspector.prototype._avoFunctionTrackSchemaFromEvent = function (eventName, eventProperties, eventId, eventHash) {
|
|
197
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
198
|
+
var eventSchema, validationResult, schemaWithValidation, e_2;
|
|
199
|
+
return __generator(this, function (_a) {
|
|
200
|
+
switch (_a.label) {
|
|
201
|
+
case 0:
|
|
202
|
+
_a.trys.push([0, 4, , 5]);
|
|
203
|
+
if (!this.avoDeduplicator.shouldRegisterEvent(eventName, eventProperties, true)) return [3 /*break*/, 2];
|
|
204
|
+
if (AvoInspector.shouldLog) {
|
|
205
|
+
console.log("Avo Inspector: supplied event " +
|
|
206
|
+
eventName +
|
|
207
|
+
" with params " +
|
|
208
|
+
JSON.stringify(eventProperties));
|
|
209
|
+
}
|
|
210
|
+
eventSchema = this.extractSchema(eventProperties, false);
|
|
211
|
+
return [4 /*yield*/, this.fetchAndValidateEvent(eventName, eventProperties)];
|
|
212
|
+
case 1:
|
|
213
|
+
validationResult = _a.sent();
|
|
214
|
+
if (validationResult) {
|
|
215
|
+
schemaWithValidation = this.mergeValidationResults(eventSchema, validationResult);
|
|
216
|
+
// Send validated events immediately, bypassing the batcher
|
|
217
|
+
this.sendEventWithValidation(eventName, schemaWithValidation, eventId, eventHash, validationResult, eventProperties);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
this.trackSchemaInternal(eventName, eventSchema, eventId, eventHash, eventProperties);
|
|
221
|
+
}
|
|
222
|
+
return [2 /*return*/, eventSchema];
|
|
223
|
+
case 2:
|
|
224
|
+
if (AvoInspector.shouldLog) {
|
|
225
|
+
console.log("Avo Inspector: Deduplicated event: " + eventName);
|
|
226
|
+
}
|
|
227
|
+
return [2 /*return*/, []];
|
|
228
|
+
case 3: return [3 /*break*/, 5];
|
|
229
|
+
case 4:
|
|
230
|
+
e_2 = _a.sent();
|
|
231
|
+
console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e_2);
|
|
232
|
+
return [2 /*return*/, []];
|
|
233
|
+
case 5: return [2 /*return*/];
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
AvoInspector.prototype.trackSchema = function (eventName, eventSchema) {
|
|
206
239
|
try {
|
|
207
|
-
if (
|
|
240
|
+
if (this.avoDeduplicator.shouldRegisterSchemaFromManually(eventName, eventSchema)) {
|
|
208
241
|
if (AvoInspector.shouldLog) {
|
|
209
242
|
console.log("Avo Inspector: supplied event " +
|
|
210
243
|
eventName +
|
|
211
244
|
" with schema " +
|
|
212
245
|
JSON.stringify(eventSchema));
|
|
213
246
|
}
|
|
214
|
-
// For trackSchema we don't have raw properties, so we can't validate
|
|
215
|
-
// Just fetch/cache spec for future use and use batched flow
|
|
216
|
-
await this.fetchEventSpecIfNeeded(eventName);
|
|
217
247
|
this.trackSchemaInternal(eventName, eventSchema, null, null);
|
|
218
248
|
}
|
|
219
249
|
else {
|
|
@@ -225,23 +255,24 @@ class AvoInspector {
|
|
|
225
255
|
catch (e) {
|
|
226
256
|
console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
|
|
227
257
|
}
|
|
228
|
-
}
|
|
229
|
-
trackSchemaInternal(eventName, eventSchema, eventId, eventHash) {
|
|
258
|
+
};
|
|
259
|
+
AvoInspector.prototype.trackSchemaInternal = function (eventName, eventSchema, eventId, eventHash, eventProperties) {
|
|
230
260
|
try {
|
|
231
|
-
this.avoBatcher.handleTrackSchema(eventName, eventSchema, eventId, eventHash);
|
|
261
|
+
this.avoBatcher.handleTrackSchema(eventName, eventSchema, eventId, eventHash, eventProperties);
|
|
232
262
|
}
|
|
233
263
|
catch (e) {
|
|
234
264
|
console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
|
|
235
265
|
}
|
|
236
|
-
}
|
|
237
|
-
enableLogging(enable) {
|
|
266
|
+
};
|
|
267
|
+
AvoInspector.prototype.enableLogging = function (enable) {
|
|
238
268
|
AvoInspector._shouldLog = enable;
|
|
239
|
-
}
|
|
240
|
-
|
|
269
|
+
};
|
|
270
|
+
AvoInspector.prototype.extractSchema = function (eventProperties, shouldLogIfEnabled) {
|
|
271
|
+
if (shouldLogIfEnabled === void 0) { shouldLogIfEnabled = true; }
|
|
241
272
|
try {
|
|
242
273
|
if (this.avoDeduplicator.hasSeenEventParams(eventProperties, true)) {
|
|
243
274
|
if (shouldLogIfEnabled && AvoInspector.shouldLog) {
|
|
244
|
-
console.warn("Avo Inspector: WARNING! You are trying to extract schema shape that was just reported by your Avo
|
|
275
|
+
console.warn("Avo Inspector: WARNING! You are trying to extract schema shape that was just reported by your Avo functions. " +
|
|
245
276
|
"This is an indicator of duplicate inspector reporting. " +
|
|
246
277
|
"Please reach out to support@avo.app for advice if you are not sure how to handle this.");
|
|
247
278
|
}
|
|
@@ -250,172 +281,125 @@ class AvoInspector {
|
|
|
250
281
|
console.log("Avo Inspector: extracting schema from " +
|
|
251
282
|
JSON.stringify(eventProperties));
|
|
252
283
|
}
|
|
253
|
-
return
|
|
284
|
+
return AvoSchemaParser_1.AvoSchemaParser.extractSchema(eventProperties);
|
|
254
285
|
}
|
|
255
286
|
catch (e) {
|
|
256
|
-
console.error("Avo Inspector: something went wrong
|
|
287
|
+
console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
|
|
257
288
|
return [];
|
|
258
289
|
}
|
|
259
|
-
}
|
|
260
|
-
setBatchSize(newBatchSize) {
|
|
290
|
+
};
|
|
291
|
+
AvoInspector.prototype.setBatchSize = function (newBatchSize) {
|
|
261
292
|
AvoInspector._batchSize = newBatchSize;
|
|
262
|
-
}
|
|
263
|
-
setBatchFlushSeconds(newBatchFlushSeconds) {
|
|
293
|
+
};
|
|
294
|
+
AvoInspector.prototype.setBatchFlushSeconds = function (newBatchFlushSeconds) {
|
|
264
295
|
AvoInspector._batchFlushSeconds = newBatchFlushSeconds;
|
|
265
|
-
}
|
|
296
|
+
};
|
|
266
297
|
/**
|
|
267
298
|
* Handles branch change detection and cache storage for a fetched event spec.
|
|
268
|
-
* This logic is shared between fetchEventSpecIfNeeded and fetchAndValidateEvent.
|
|
269
299
|
*/
|
|
270
|
-
handleBranchChangeAndCache(specResponse, eventName) {
|
|
271
|
-
|
|
272
|
-
|
|
300
|
+
AvoInspector.prototype.handleBranchChangeAndCache = function (specResponse, eventName) {
|
|
301
|
+
var _a;
|
|
302
|
+
var newBranchId = specResponse.metadata.branchId;
|
|
273
303
|
if (this.currentBranchId !== null && this.currentBranchId !== newBranchId) {
|
|
274
304
|
if (AvoInspector.shouldLog) {
|
|
275
|
-
console.log(
|
|
305
|
+
console.log("[Avo Inspector] Branch changed from ".concat(this.currentBranchId, " to ").concat(newBranchId, ". Flushing cache."));
|
|
276
306
|
}
|
|
277
|
-
this.eventSpecCache
|
|
307
|
+
(_a = this.eventSpecCache) === null || _a === void 0 ? void 0 : _a.clear();
|
|
278
308
|
}
|
|
279
309
|
this.currentBranchId = newBranchId;
|
|
280
|
-
// Store in cache
|
|
281
310
|
if (this.eventSpecCache && this.streamId) {
|
|
282
311
|
this.eventSpecCache.set(this.apiKey, this.streamId, eventName, specResponse);
|
|
283
312
|
}
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Fetches the event spec if spec fetching is enabled.
|
|
287
|
-
* Used by trackSchema when we don't have raw properties to validate.
|
|
288
|
-
*
|
|
289
|
-
* Note: EventSpec fetching only happens in dev/staging environments.
|
|
290
|
-
*/
|
|
291
|
-
async fetchEventSpecIfNeeded(eventName) {
|
|
292
|
-
const spec = await this.ensureEventSpecReady();
|
|
293
|
-
if (!spec)
|
|
294
|
-
return;
|
|
295
|
-
try {
|
|
296
|
-
// Check cache first (includes cached empty responses)
|
|
297
|
-
if (spec.cache.contains(this.apiKey, spec.streamId, eventName)) {
|
|
298
|
-
const cached = spec.cache.get(this.apiKey, spec.streamId, eventName);
|
|
299
|
-
if (!cached) {
|
|
300
|
-
// Cached empty response — no spec exists for this event
|
|
301
|
-
if (AvoInspector.shouldLog) {
|
|
302
|
-
console.log(`[Avo Inspector] Cache hit (empty) for event: ${eventName}. Sending without validation.`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
// Cache miss - fetch from API (blocking)
|
|
308
|
-
const specResponse = await spec.fetcher.fetch({
|
|
309
|
-
apiKey: this.apiKey,
|
|
310
|
-
streamId: spec.streamId,
|
|
311
|
-
eventName
|
|
312
|
-
});
|
|
313
|
-
if (specResponse) {
|
|
314
|
-
this.handleBranchChangeAndCache(specResponse, eventName);
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
// Cache the empty response so we don't re-fetch
|
|
318
|
-
spec.cache.set(this.apiKey, spec.streamId, eventName, null);
|
|
319
|
-
if (AvoInspector.shouldLog) {
|
|
320
|
-
console.log(`[Avo Inspector] Event spec fetch returned null for event: ${eventName}. Cached empty response.`);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
catch (error) {
|
|
325
|
-
// Graceful degradation - log but don't fail
|
|
326
|
-
if (AvoInspector.shouldLog) {
|
|
327
|
-
console.error(`[Avo Inspector] Error fetching event spec for ${eventName}:`, error);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
313
|
+
};
|
|
331
314
|
/**
|
|
332
315
|
* Fetches event spec and validates the event against it.
|
|
333
316
|
* Returns ValidationResult if spec is available, null otherwise.
|
|
334
|
-
*
|
|
335
|
-
* Note: EventSpec fetching and validation only happens in dev/staging environments.
|
|
317
|
+
* Only runs in dev/staging environments.
|
|
336
318
|
*/
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
return
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
319
|
+
AvoInspector.prototype.fetchAndValidateEvent = function (eventName, eventProperties) {
|
|
320
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
321
|
+
var specResponse, fetched, validationResult, error_1;
|
|
322
|
+
return __generator(this, function (_a) {
|
|
323
|
+
switch (_a.label) {
|
|
324
|
+
case 0:
|
|
325
|
+
// Only fetch specs in dev/staging environments (NOT in production)
|
|
326
|
+
if (this.environment === AvoInspectorEnv_1.AvoInspectorEnv.Prod) {
|
|
327
|
+
return [2 /*return*/, null];
|
|
328
|
+
}
|
|
329
|
+
// Only fetch if we have the required infrastructure
|
|
330
|
+
if (!this.eventSpecCache || !this.eventSpecFetcher || !this.streamId) {
|
|
331
|
+
return [2 /*return*/, null];
|
|
332
|
+
}
|
|
333
|
+
_a.label = 1;
|
|
334
|
+
case 1:
|
|
335
|
+
_a.trys.push([1, 6, , 7]);
|
|
336
|
+
specResponse = this.eventSpecCache.get(this.apiKey, this.streamId, eventName);
|
|
337
|
+
if (!(specResponse === undefined)) return [3 /*break*/, 3];
|
|
338
|
+
return [4 /*yield*/, this.eventSpecFetcher.fetch({
|
|
339
|
+
apiKey: this.apiKey,
|
|
340
|
+
streamId: this.streamId,
|
|
341
|
+
eventName: eventName
|
|
342
|
+
})];
|
|
343
|
+
case 2:
|
|
344
|
+
fetched = _a.sent();
|
|
345
|
+
if (fetched) {
|
|
346
|
+
this.handleBranchChangeAndCache(fetched, eventName);
|
|
347
|
+
specResponse = fetched;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
// Cache null response to prevent re-fetching
|
|
351
|
+
this.eventSpecCache.set(this.apiKey, this.streamId, eventName, null);
|
|
352
|
+
specResponse = null;
|
|
353
|
+
}
|
|
354
|
+
_a.label = 3;
|
|
355
|
+
case 3:
|
|
356
|
+
if (!specResponse) return [3 /*break*/, 5];
|
|
357
|
+
return [4 /*yield*/, (0, EventValidator_1.validateEvent)(eventProperties, specResponse)];
|
|
358
|
+
case 4:
|
|
359
|
+
validationResult = _a.sent();
|
|
360
|
+
return [2 /*return*/, validationResult];
|
|
361
|
+
case 5: return [2 /*return*/, null];
|
|
362
|
+
case 6:
|
|
363
|
+
error_1 = _a.sent();
|
|
364
|
+
if (AvoInspector.shouldLog) {
|
|
365
|
+
console.error("[Avo Inspector] Error validating event ".concat(eventName, ":"), error_1);
|
|
366
|
+
}
|
|
367
|
+
return [2 /*return*/, null];
|
|
368
|
+
case 7: return [2 /*return*/];
|
|
350
369
|
}
|
|
351
|
-
else {
|
|
352
|
-
// Cached empty response — no spec exists for this event
|
|
353
|
-
if (AvoInspector.shouldLog) {
|
|
354
|
-
console.log(`[Avo Inspector] Cache hit (empty) for event: ${eventName}. Sending without validation.`);
|
|
355
|
-
}
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
// Cache miss - fetch from API (blocking)
|
|
360
|
-
const specResponse = await spec.fetcher.fetch({
|
|
361
|
-
apiKey: this.apiKey,
|
|
362
|
-
streamId: spec.streamId,
|
|
363
|
-
eventName
|
|
364
370
|
});
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return spec.validate(eventProperties, specResponse);
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
// Cache the empty response so we don't re-fetch
|
|
371
|
-
spec.cache.set(this.apiKey, spec.streamId, eventName, null);
|
|
372
|
-
if (AvoInspector.shouldLog) {
|
|
373
|
-
console.log(`[Avo Inspector] Event spec fetch returned null for event: ${eventName}. Cached empty response.`);
|
|
374
|
-
}
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
catch (error) {
|
|
379
|
-
// Graceful degradation - log but don't fail
|
|
380
|
-
if (AvoInspector.shouldLog) {
|
|
381
|
-
console.error(`[Avo Inspector] Error validating event ${eventName}:`, error);
|
|
382
|
-
}
|
|
383
|
-
return null;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
371
|
+
});
|
|
372
|
+
};
|
|
386
373
|
/**
|
|
387
374
|
* Merges validation results into the event schema.
|
|
388
375
|
* Adds failedEventIds or passedEventIds to each property based on validation.
|
|
389
|
-
* Recursively merges validation results for nested children.
|
|
390
376
|
*/
|
|
391
|
-
mergeValidationResults(eventSchema, validationResult) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
377
|
+
AvoInspector.prototype.mergeValidationResults = function (eventSchema, validationResult) {
|
|
378
|
+
var _this = this;
|
|
379
|
+
return eventSchema.map(function (prop) {
|
|
380
|
+
var propValidation = validationResult.propertyResults[prop.propertyName];
|
|
381
|
+
return _this.mergePropertyValidation(prop, propValidation);
|
|
395
382
|
});
|
|
396
|
-
}
|
|
383
|
+
};
|
|
397
384
|
/**
|
|
398
385
|
* Merges validation result into a single property, recursively handling children.
|
|
399
386
|
*/
|
|
400
|
-
mergePropertyValidation(prop, propValidation) {
|
|
401
|
-
|
|
387
|
+
AvoInspector.prototype.mergePropertyValidation = function (prop, propValidation) {
|
|
388
|
+
var _this = this;
|
|
389
|
+
var result = {
|
|
402
390
|
propertyName: prop.propertyName,
|
|
403
391
|
propertyType: prop.propertyType
|
|
404
392
|
};
|
|
405
|
-
if (prop.encryptedPropertyValue) {
|
|
406
|
-
result.encryptedPropertyValue = prop.encryptedPropertyValue;
|
|
407
|
-
}
|
|
408
393
|
// Recursively merge validation results into children
|
|
409
394
|
if (prop.children && Array.isArray(prop.children)) {
|
|
410
|
-
result.children = prop.children.map((child)
|
|
411
|
-
|
|
395
|
+
result.children = prop.children.map(function (child) {
|
|
396
|
+
var _a;
|
|
412
397
|
if (typeof child === 'string') {
|
|
413
398
|
return child;
|
|
414
399
|
}
|
|
415
400
|
if (child && typeof child === 'object' && child.propertyName) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return this.mergePropertyValidation(child, childValidation);
|
|
401
|
+
var childValidation = (_a = propValidation === null || propValidation === void 0 ? void 0 : propValidation.children) === null || _a === void 0 ? void 0 : _a[child.propertyName];
|
|
402
|
+
return _this.mergePropertyValidation(child, childValidation);
|
|
419
403
|
}
|
|
420
404
|
return child;
|
|
421
405
|
});
|
|
@@ -430,45 +414,22 @@ class AvoInspector {
|
|
|
430
414
|
}
|
|
431
415
|
}
|
|
432
416
|
return result;
|
|
433
|
-
}
|
|
417
|
+
};
|
|
434
418
|
/**
|
|
435
|
-
* Sends
|
|
436
|
-
*
|
|
419
|
+
* Sends a validated event immediately, bypassing the batcher.
|
|
420
|
+
* Matches Android SDK's sendEventWithValidation behavior.
|
|
437
421
|
*/
|
|
438
|
-
sendEventWithValidation(eventName, eventSchema, eventId, eventHash, validationResult) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
.filter((p) => p.failedEventIds && p.failedEventIds.length > 0)
|
|
445
|
-
.map((p) => ({
|
|
446
|
-
property: p.propertyName,
|
|
447
|
-
failedEventIds: p.failedEventIds
|
|
448
|
-
})));
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
// Create the event body with validation data
|
|
452
|
-
const eventBody = this.avoNetworkCallsHandler.bodyForEventSchemaCall(eventName, eventSchema, eventId, eventHash, validationResult.metadata ?? undefined, validationResult.metadata?.branchId);
|
|
453
|
-
// Send immediately (bypass batching)
|
|
454
|
-
this.avoNetworkCallsHandler.callInspectorImmediately(eventBody, (error) => {
|
|
455
|
-
if (error) {
|
|
456
|
-
if (AvoInspector.shouldLog) {
|
|
457
|
-
console.error(`[Avo Inspector] Failed to send event "${eventName}" with validation:`, error);
|
|
458
|
-
}
|
|
459
|
-
// Fallback: add to batch on failure (without validation data)
|
|
460
|
-
this.avoBatcher.handleTrackSchema(eventName, eventSchema, eventId, eventHash);
|
|
461
|
-
}
|
|
462
|
-
else {
|
|
463
|
-
if (AvoInspector.shouldLog) {
|
|
464
|
-
console.log(`[Avo Inspector] Event "${eventName}" sent successfully with validation`);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
422
|
+
AvoInspector.prototype.sendEventWithValidation = function (eventName, eventSchema, eventId, eventHash, validationResult, eventProperties) {
|
|
423
|
+
var networkHandler = this.avoBatcher.networkCallsHandler;
|
|
424
|
+
networkHandler.bodyForEventSchemaCall(eventName, eventSchema, eventId, eventHash, eventProperties, validationResult.metadata).then(function (eventBody) {
|
|
425
|
+
networkHandler.reportValidatedEvent(eventBody);
|
|
426
|
+
}).catch(function (e) {
|
|
427
|
+
console.error("Avo Inspector: failed to send validated event.", e);
|
|
467
428
|
});
|
|
468
|
-
}
|
|
469
|
-
|
|
429
|
+
};
|
|
430
|
+
AvoInspector._batchSize = 30;
|
|
431
|
+
AvoInspector._batchFlushSeconds = 30;
|
|
432
|
+
AvoInspector._shouldLog = false;
|
|
433
|
+
return AvoInspector;
|
|
434
|
+
}());
|
|
470
435
|
exports.AvoInspector = AvoInspector;
|
|
471
|
-
AvoInspector._batchSize = 30;
|
|
472
|
-
AvoInspector._batchFlushSeconds = 30;
|
|
473
|
-
AvoInspector._shouldLog = false;
|
|
474
|
-
AvoInspector._networkTimeout = 2000;
|