avo-inspector 3.0.1 → 3.1.0-alpha.2

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.
Files changed (166) hide show
  1. package/README.md +32 -0
  2. package/dist/AvoBatcher.js +79 -0
  3. package/dist/AvoDeduplicator.d.ts +12 -4
  4. package/{dist-native → dist}/AvoDeduplicator.js +45 -46
  5. package/dist/AvoEncryption.js +282 -0
  6. package/{dist-native → dist}/AvoGuid.js +2 -1
  7. package/dist/AvoInspector.js +474 -0
  8. package/{dist-native → dist}/AvoInspectorEnv.js +1 -1
  9. package/dist/AvoNetworkCallsHandler.js +185 -0
  10. package/dist/AvoSchemaParser.js +132 -0
  11. package/dist/AvoStorage.js +256 -0
  12. package/dist/AvoStreamId.js +144 -0
  13. package/{dist-browser → dist}/__mocks__/xhr.d.ts +0 -1
  14. package/{dist-browser → dist}/__mocks__/xhrError.d.ts +0 -1
  15. package/dist/__tests__/helpers/encryptionHelpers.d.ts +24 -0
  16. package/dist/__tests__/helpers/mockFactories.d.ts +68 -0
  17. package/dist/esm/AvoBatcher.js +75 -0
  18. package/dist/esm/AvoDeduplicator.js +137 -0
  19. package/dist/esm/AvoEncryption.js +277 -0
  20. package/dist/esm/AvoGuid.js +9 -0
  21. package/dist/esm/AvoInspector.js +470 -0
  22. package/dist/esm/AvoInspectorEnv.js +5 -0
  23. package/dist/esm/AvoNetworkCallsHandler.js +181 -0
  24. package/dist/esm/AvoSchemaParser.js +128 -0
  25. package/dist/esm/AvoStorage.js +176 -0
  26. package/dist/esm/AvoStreamId.js +140 -0
  27. package/dist/esm/eventSpec/AvoEventSpecCache.js +165 -0
  28. package/dist/esm/eventSpec/AvoEventSpecFetchTypes.js +6 -0
  29. package/dist/esm/eventSpec/AvoEventSpecFetcher.js +203 -0
  30. package/dist/esm/eventSpec/EventValidator.js +687 -0
  31. package/{dist-native/index.d.ts → dist/esm/index.js} +1 -0
  32. package/dist/esm/utils.js +38 -0
  33. package/dist/esm/version.js +2 -0
  34. package/dist/eventSpec/AvoEventSpecCache.d.ts +10 -2
  35. package/dist/eventSpec/AvoEventSpecCache.js +169 -0
  36. package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +2 -2
  37. package/dist/eventSpec/AvoEventSpecFetchTypes.js +7 -0
  38. package/dist/eventSpec/AvoEventSpecFetcher.js +207 -0
  39. package/dist/eventSpec/EventValidator.js +690 -0
  40. package/dist/eventSpec/index.js +20 -0
  41. package/dist/index.js +1 -1
  42. package/dist/lite/AvoBatcherLite.d.ts +17 -0
  43. package/{dist-native/AvoBatcher.js → dist/lite/AvoBatcherLite.js} +16 -11
  44. package/{dist-native/AvoInspector.d.ts → dist/lite/AvoInspectorLite.d.ts} +17 -20
  45. package/dist/lite/AvoInspectorLite.js +247 -0
  46. package/{dist-browser/AvoNetworkCallsHandler.d.ts → dist/lite/AvoNetworkCallsHandlerLite.d.ts} +31 -23
  47. package/dist/lite/AvoNetworkCallsHandlerLite.js +164 -0
  48. package/dist/lite/AvoSchemaParserLite.d.ts +7 -0
  49. package/dist/lite/AvoSchemaParserLite.js +170 -0
  50. package/dist/lite/index.d.ts +2 -0
  51. package/{dist-native → dist/lite}/index.js +3 -3
  52. package/{dist-native → dist}/utils.js +2 -2
  53. package/dist/version.d.ts +1 -0
  54. package/dist/version.js +5 -0
  55. package/package.json +30 -6
  56. package/{lib/ocaml/AvoInspector.res → rescript/AvoInspectorLite.res} +3 -4
  57. package/{lib/ocaml/AvoInspector.resi → rescript/AvoInspectorLite.resi} +2 -2
  58. package/rescript.json +16 -0
  59. package/.eslintrc.json +0 -24
  60. package/CHANGELOG.md +0 -95
  61. package/dist/AvoAnonymousId.d.ts +0 -26
  62. package/dist/AvoInstallationId.d.ts +0 -5
  63. package/dist/AvoSessionTracker.d.ts +0 -15
  64. package/dist/browser.js.LICENSE.txt +0 -5
  65. package/dist/eventSpec/AvoGuid.d.ts +0 -3
  66. package/dist/eventSpec/cache.d.ts +0 -73
  67. package/dist/eventSpec/types.d.ts +0 -74
  68. package/dist/index.js.LICENSE.txt +0 -3
  69. package/dist-browser/AvoAnonymousId.d.ts +0 -26
  70. package/dist-browser/AvoBatcher.d.ts +0 -26
  71. package/dist-browser/AvoDeduplicator.d.ts +0 -20
  72. package/dist-browser/AvoEncryption.d.ts +0 -33
  73. package/dist-browser/AvoGuid.d.ts +0 -3
  74. package/dist-browser/AvoInspector.d.ts +0 -78
  75. package/dist-browser/AvoInspectorEnv.d.ts +0 -7
  76. package/dist-browser/AvoSchemaParser.d.ts +0 -20
  77. package/dist-browser/AvoStorage.d.ts +0 -22
  78. package/dist-browser/__tests__/helpers/encryptionHelpers.d.ts +0 -13
  79. package/dist-browser/__tests__/helpers/mockFactories.d.ts +0 -49
  80. package/dist-browser/eventSpec/AvoEventSpecCache.d.ts +0 -73
  81. package/dist-browser/eventSpec/AvoEventSpecFetchTypes.d.ts +0 -109
  82. package/dist-browser/eventSpec/AvoEventSpecFetcher.d.ts +0 -55
  83. package/dist-browser/eventSpec/EventValidator.d.ts +0 -90
  84. package/dist-browser/index.d.ts +0 -2
  85. package/dist-browser/utils.d.ts +0 -3
  86. package/dist-native/AvoBatcher.d.ts +0 -24
  87. package/dist-native/AvoDeduplicator.d.ts +0 -36
  88. package/dist-native/AvoGuid.d.ts +0 -3
  89. package/dist-native/AvoInspector.js +0 -220
  90. package/dist-native/AvoInspectorEnv.d.ts +0 -7
  91. package/dist-native/AvoInstallationId.d.ts +0 -5
  92. package/dist-native/AvoInstallationId.js +0 -36
  93. package/dist-native/AvoNetworkCallsHandler.d.ts +0 -48
  94. package/dist-native/AvoNetworkCallsHandler.js +0 -142
  95. package/dist-native/AvoSchemaParser.d.ts +0 -11
  96. package/dist-native/AvoSchemaParser.js +0 -95
  97. package/dist-native/AvoSessionTracker.d.ts +0 -15
  98. package/dist-native/AvoSessionTracker.js +0 -104
  99. package/dist-native/AvoStorage.d.ts +0 -22
  100. package/dist-native/AvoStorage.js +0 -336
  101. package/dist-native/__mocks__/xhr.d.ts +0 -12
  102. package/dist-native/__mocks__/xhr.js +0 -14
  103. package/dist-native/__mocks__/xhrError.d.ts +0 -12
  104. package/dist-native/__mocks__/xhrError.js +0 -14
  105. package/dist-native/__tests__/AvoInspector.js +0 -207
  106. package/dist-native/__tests__/Batcher.js +0 -280
  107. package/dist-native/__tests__/BatcherPublic.d.ts +0 -1
  108. package/dist-native/__tests__/BatcherPublic.js +0 -82
  109. package/dist-native/__tests__/Deduplicator.d.ts +0 -1
  110. package/dist-native/__tests__/Deduplicator.js +0 -111
  111. package/dist-native/__tests__/InstallationId.d.ts +0 -1
  112. package/dist-native/__tests__/InstallationId.js +0 -40
  113. package/dist-native/__tests__/NetworkCallsHandler.d.ts +0 -1
  114. package/dist-native/__tests__/NetworkCallsHandler.js +0 -132
  115. package/dist-native/__tests__/Parsing.d.ts +0 -1
  116. package/dist-native/__tests__/Parsing.js +0 -115
  117. package/dist-native/__tests__/Sessions.d.ts +0 -1
  118. package/dist-native/__tests__/Sessions.js +0 -179
  119. package/dist-native/__tests__/Storage.d.ts +0 -1
  120. package/dist-native/__tests__/Storage.js +0 -173
  121. package/dist-native/__tests__/constants.d.ts +0 -36
  122. package/dist-native/__tests__/constants.js +0 -47
  123. package/dist-native/utils.d.ts +0 -3
  124. package/jest.config.js +0 -33
  125. package/lib/bs/.bsbuild +0 -5
  126. package/lib/bs/.bsdeps +0 -8
  127. package/lib/bs/.compiler.log +0 -2
  128. package/lib/bs/.ninja_log +0 -7
  129. package/lib/bs/build.ninja +0 -19
  130. package/lib/bs/install.ninja +0 -12
  131. package/lib/bs/rescript/AvoInspector.ast +0 -0
  132. package/lib/bs/rescript/AvoInspector.cmi +0 -0
  133. package/lib/bs/rescript/AvoInspector.cmj +0 -0
  134. package/lib/bs/rescript/AvoInspector.cmt +0 -0
  135. package/lib/bs/rescript/AvoInspector.cmti +0 -0
  136. package/lib/bs/rescript/AvoInspector.d +0 -0
  137. package/lib/bs/rescript/AvoInspector.iast +0 -0
  138. package/lib/ocaml/.compiler.log +0 -2
  139. package/lib/ocaml/.ninja_log +0 -8
  140. package/lib/ocaml/AvoInspector.cmi +0 -0
  141. package/lib/ocaml/AvoInspector.cmj +0 -0
  142. package/lib/ocaml/AvoInspector.cmt +0 -0
  143. package/lib/ocaml/AvoInspector.cmti +0 -0
  144. package/lib/ocaml/install.stamp +0 -0
  145. package/rescript/AvoInspector.mjs +0 -11
  146. package/webpack.browserConfig.js +0 -26
  147. package/webpack.config.js +0 -26
  148. /package/{dist-browser → dist}/__tests__/AvoEncryption_test.d.ts +0 -0
  149. /package/{dist-browser → dist}/__tests__/AvoInspectorEventSpec_test.d.ts +0 -0
  150. /package/{dist-browser/__tests__/AnonymousId_test.d.ts → dist/__tests__/AvoInspectorLite_test.d.ts} +0 -0
  151. /package/{dist-browser → dist}/__tests__/AvoInspector_test.d.ts +0 -0
  152. /package/{dist-browser/__tests__/BatcherPublic_test.d.ts → dist/__tests__/AvoSchemaParserLite_test.d.ts} +0 -0
  153. /package/{dist-browser/__tests__/Batcher_test.d.ts → dist/__tests__/BatcherPublic_test.d.ts} +0 -0
  154. /package/{dist-browser/__tests__/Deduplicator_test.d.ts → dist/__tests__/Batcher_test.d.ts} +0 -0
  155. /package/{dist-browser/__tests__/EncryptedPropertyTracking_test.d.ts → dist/__tests__/Deduplicator_test.d.ts} +0 -0
  156. /package/{dist-browser/__tests__/EventSpecCache_test.d.ts → dist/__tests__/EncryptedPropertyTracking_test.d.ts} +0 -0
  157. /package/{dist-browser/__tests__/EventSpecFetcher_test.d.ts → dist/__tests__/EventSpecCache_test.d.ts} +0 -0
  158. /package/{dist-browser/__tests__/EventValidator_test.d.ts → dist/__tests__/EventSpecFetcher_test.d.ts} +0 -0
  159. /package/{dist-browser/__tests__/NetworkCallsHandler_test.d.ts → dist/__tests__/EventValidator_test.d.ts} +0 -0
  160. /package/{dist-browser/__tests__/Parsing_test.d.ts → dist/__tests__/NetworkCallsHandler_test.d.ts} +0 -0
  161. /package/{dist-browser/__tests__/Storage_test.d.ts → dist/__tests__/Parsing_test.d.ts} +0 -0
  162. /package/{dist-browser/__tests__/ValidationIntegration_test.d.ts → dist/__tests__/Storage_test.d.ts} +0 -0
  163. /package/{dist-native/__tests__/AvoInspector.d.ts → dist/__tests__/StreamId_test.d.ts} +0 -0
  164. /package/{dist-native/__tests__/Batcher.d.ts → dist/__tests__/ValidationIntegration_test.d.ts} +0 -0
  165. /package/{dist-browser → dist}/__tests__/constants.d.ts +0 -0
  166. /package/{dist-browser/eventSpec/index.d.ts → dist/esm/eventSpec/index.js} +0 -0
@@ -0,0 +1,474 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AvoInspector = void 0;
4
+ const AvoInspectorEnv_1 = require("./AvoInspectorEnv");
5
+ const AvoSchemaParser_1 = require("./AvoSchemaParser");
6
+ const AvoBatcher_1 = require("./AvoBatcher");
7
+ const AvoNetworkCallsHandler_1 = require("./AvoNetworkCallsHandler");
8
+ const AvoStorage_1 = require("./AvoStorage");
9
+ const AvoDeduplicator_1 = require("./AvoDeduplicator");
10
+ const AvoStreamId_1 = require("./AvoStreamId");
11
+ const utils_1 = require("./utils");
12
+ const version_1 = require("./version");
13
+ const libVersion = version_1.LIB_VERSION;
14
+ class AvoInspector {
15
+ static get batchSize() {
16
+ return this._batchSize;
17
+ }
18
+ static set batchSize(newSize) {
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) {
42
+ this.currentBranchId = null;
43
+ // the constructor does aggressive null/undefined checking because same code paths will be accessible from JS
44
+ if ((0, utils_1.isValueEmpty)(options.env)) {
45
+ this.environment = AvoInspectorEnv_1.AvoInspectorEnv.Dev;
46
+ console.warn("[Avo Inspector] No environment provided. Defaulting to dev.");
47
+ }
48
+ else if (!Object.values(AvoInspectorEnv_1.AvoInspectorEnv).includes(options.env)) {
49
+ this.environment = AvoInspectorEnv_1.AvoInspectorEnv.Dev;
50
+ console.warn("[Avo Inspector] Unsupported environment provided. Defaulting to dev. Supported environments - Dev, Staging, Prod.");
51
+ }
52
+ else {
53
+ this.environment = options.env;
54
+ }
55
+ if ((0, utils_1.isValueEmpty)(options.apiKey)) {
56
+ throw new Error("[Avo Inspector] No API key provided. Inspector can't operate without API key.");
57
+ }
58
+ else {
59
+ this.apiKey = options.apiKey;
60
+ }
61
+ if ((0, utils_1.isValueEmpty)(options.version)) {
62
+ throw new Error("[Avo Inspector] No version provided. Many features of Inspector rely on versioning. Please provide comparable string version, i.e. integer or semantic.");
63
+ }
64
+ else {
65
+ this.version = options.version;
66
+ }
67
+ if (this.environment === AvoInspectorEnv_1.AvoInspectorEnv.Dev) {
68
+ AvoInspector._batchSize = 1;
69
+ AvoInspector._shouldLog = true;
70
+ }
71
+ else {
72
+ AvoInspector._batchSize = 30;
73
+ AvoInspector._batchFlushSeconds = 30;
74
+ AvoInspector._shouldLog = false;
75
+ }
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
+ try {
97
+ const [{ EventSpecCache }, { AvoEventSpecFetcher }, { validateEvent }] = await Promise.all([
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
+ }
111
+ }
112
+ catch (error) {
113
+ if (AvoInspector._shouldLog) {
114
+ console.error("[Avo Inspector] Failed to load event spec modules:", error);
115
+ }
116
+ }
117
+ finally {
118
+ this._eventSpecReady = undefined;
119
+ }
120
+ }
121
+ async ensureEventSpecReady() {
122
+ if (this.environment === AvoInspectorEnv_1.AvoInspectorEnv.Prod)
123
+ return null;
124
+ if (this._eventSpecReady)
125
+ await this._eventSpecReady;
126
+ if (!this.eventSpecCache || !this.eventSpecFetcher || !this.streamId || !this._validateEvent)
127
+ return null;
128
+ return {
129
+ cache: this.eventSpecCache,
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);
155
+ }
156
+ return eventSchema;
157
+ }
158
+ else {
159
+ if (AvoInspector.shouldLog) {
160
+ console.log("Avo Inspector: Deduplicated event: " + eventName);
161
+ }
162
+ return [];
163
+ }
164
+ }
165
+ catch (e) {
166
+ console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
167
+ return [];
168
+ }
169
+ }
170
+ async _avoFunctionTrackSchemaFromEvent(eventName, eventProperties, eventId, eventHash) {
171
+ try {
172
+ if (this.avoDeduplicator.shouldRegisterEvent(eventName, eventProperties, true)) {
173
+ if (AvoInspector.shouldLog) {
174
+ console.log("Avo Inspector: supplied event " +
175
+ eventName +
176
+ " with params " +
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;
192
+ }
193
+ else {
194
+ if (AvoInspector.shouldLog) {
195
+ console.log("Avo Inspector: Deduplicated event: " + eventName);
196
+ }
197
+ return [];
198
+ }
199
+ }
200
+ catch (e) {
201
+ console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
202
+ return [];
203
+ }
204
+ }
205
+ async trackSchema(eventName, eventSchema) {
206
+ try {
207
+ if (await this.avoDeduplicator.shouldRegisterSchemaFromManually(eventName, eventSchema)) {
208
+ if (AvoInspector.shouldLog) {
209
+ console.log("Avo Inspector: supplied event " +
210
+ eventName +
211
+ " with schema " +
212
+ JSON.stringify(eventSchema));
213
+ }
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
+ this.trackSchemaInternal(eventName, eventSchema, null, null);
218
+ }
219
+ else {
220
+ if (AvoInspector.shouldLog) {
221
+ console.log("Avo Inspector: Deduplicated event: " + eventName);
222
+ }
223
+ }
224
+ }
225
+ catch (e) {
226
+ console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
227
+ }
228
+ }
229
+ trackSchemaInternal(eventName, eventSchema, eventId, eventHash) {
230
+ try {
231
+ this.avoBatcher.handleTrackSchema(eventName, eventSchema, eventId, eventHash);
232
+ }
233
+ catch (e) {
234
+ console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
235
+ }
236
+ }
237
+ enableLogging(enable) {
238
+ AvoInspector._shouldLog = enable;
239
+ }
240
+ async extractSchema(eventProperties, shouldLogIfEnabled = true) {
241
+ try {
242
+ if (this.avoDeduplicator.hasSeenEventParams(eventProperties, true)) {
243
+ if (shouldLogIfEnabled && AvoInspector.shouldLog) {
244
+ console.warn("Avo Inspector: WARNING! You are trying to extract schema shape that was just reported by your Avo Codegen. " +
245
+ "This is an indicator of duplicate inspector reporting. " +
246
+ "Please reach out to support@avo.app for advice if you are not sure how to handle this.");
247
+ }
248
+ }
249
+ if (AvoInspector.shouldLog) {
250
+ console.log("Avo Inspector: extracting schema from " +
251
+ JSON.stringify(eventProperties));
252
+ }
253
+ return await AvoSchemaParser_1.AvoSchemaParser.extractSchema(eventProperties, this.publicEncryptionKey, this.environment);
254
+ }
255
+ catch (e) {
256
+ console.error("Avo Inspector: something went wrong in extractSchema. Please report to support@avo.app.", e);
257
+ return [];
258
+ }
259
+ }
260
+ setBatchSize(newBatchSize) {
261
+ AvoInspector._batchSize = newBatchSize;
262
+ }
263
+ setBatchFlushSeconds(newBatchFlushSeconds) {
264
+ AvoInspector._batchFlushSeconds = newBatchFlushSeconds;
265
+ }
266
+ /**
267
+ * Handles branch change detection and cache storage for a fetched event spec.
268
+ * This logic is shared between fetchEventSpecIfNeeded and fetchAndValidateEvent.
269
+ */
270
+ handleBranchChangeAndCache(specResponse, eventName) {
271
+ // Check for branch change
272
+ const newBranchId = specResponse.metadata.branchId;
273
+ if (this.currentBranchId !== null && this.currentBranchId !== newBranchId) {
274
+ if (AvoInspector.shouldLog) {
275
+ console.log(`[Avo Inspector] Branch changed from ${this.currentBranchId} to ${newBranchId}. Flushing cache.`);
276
+ }
277
+ this.eventSpecCache?.clear();
278
+ }
279
+ this.currentBranchId = newBranchId;
280
+ // Store in cache
281
+ if (this.eventSpecCache && this.streamId) {
282
+ this.eventSpecCache.set(this.apiKey, this.streamId, eventName, specResponse);
283
+ }
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
+ }
331
+ /**
332
+ * Fetches event spec and validates the event against it.
333
+ * Returns ValidationResult if spec is available, null otherwise.
334
+ *
335
+ * Note: EventSpec fetching and validation only happens in dev/staging environments.
336
+ */
337
+ async fetchAndValidateEvent(eventName, eventProperties) {
338
+ const spec = await this.ensureEventSpecReady();
339
+ if (!spec)
340
+ return null;
341
+ try {
342
+ // Check cache first (includes cached empty responses)
343
+ if (spec.cache.contains(this.apiKey, spec.streamId, eventName)) {
344
+ const cached = spec.cache.get(this.apiKey, spec.streamId, eventName);
345
+ if (cached) {
346
+ if (AvoInspector.shouldLog) {
347
+ console.log(`[Avo Inspector] Cache hit for event: ${eventName}`);
348
+ }
349
+ return spec.validate(eventProperties, cached);
350
+ }
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
+ });
365
+ if (specResponse) {
366
+ this.handleBranchChangeAndCache(specResponse, eventName);
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
+ }
386
+ /**
387
+ * Merges validation results into the event schema.
388
+ * Adds failedEventIds or passedEventIds to each property based on validation.
389
+ * Recursively merges validation results for nested children.
390
+ */
391
+ mergeValidationResults(eventSchema, validationResult) {
392
+ return eventSchema.map((prop) => {
393
+ const propValidation = validationResult.propertyResults[prop.propertyName];
394
+ return this.mergePropertyValidation(prop, propValidation);
395
+ });
396
+ }
397
+ /**
398
+ * Merges validation result into a single property, recursively handling children.
399
+ */
400
+ mergePropertyValidation(prop, propValidation) {
401
+ const result = {
402
+ propertyName: prop.propertyName,
403
+ propertyType: prop.propertyType
404
+ };
405
+ if (prop.encryptedPropertyValue) {
406
+ result.encryptedPropertyValue = prop.encryptedPropertyValue;
407
+ }
408
+ // Recursively merge validation results into children
409
+ if (prop.children && Array.isArray(prop.children)) {
410
+ result.children = prop.children.map((child) => {
411
+ // Children can be strings (for array types) or objects (for nested properties)
412
+ if (typeof child === 'string') {
413
+ return child;
414
+ }
415
+ if (child && typeof child === 'object' && child.propertyName) {
416
+ // Get nested validation result for this child
417
+ const childValidation = propValidation?.children?.[child.propertyName];
418
+ return this.mergePropertyValidation(child, childValidation);
419
+ }
420
+ return child;
421
+ });
422
+ }
423
+ // Add validation result for this property
424
+ if (propValidation) {
425
+ if (propValidation.failedEventIds) {
426
+ result.failedEventIds = propValidation.failedEventIds;
427
+ }
428
+ if (propValidation.passedEventIds) {
429
+ result.passedEventIds = propValidation.passedEventIds;
430
+ }
431
+ }
432
+ return result;
433
+ }
434
+ /**
435
+ * Sends an event immediately with validation data (bypasses batching).
436
+ * Logs validation info if shouldLog is true.
437
+ */
438
+ sendEventWithValidation(eventName, eventSchema, eventId, eventHash, validationResult) {
439
+ // Log validation info if shouldLog is enabled
440
+ if (AvoInspector.shouldLog) {
441
+ const hasFailures = eventSchema.some((p) => p.failedEventIds && p.failedEventIds.length > 0);
442
+ if (hasFailures) {
443
+ console.log(`[Avo Inspector] Validation failures for event "${eventName}":`, eventSchema
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
+ }
467
+ });
468
+ }
469
+ }
470
+ exports.AvoInspector = AvoInspector;
471
+ AvoInspector._batchSize = 30;
472
+ AvoInspector._batchFlushSeconds = 30;
473
+ AvoInspector._shouldLog = false;
474
+ AvoInspector._networkTimeout = 2000;
@@ -4,5 +4,5 @@ exports.AvoInspectorEnv = void 0;
4
4
  exports.AvoInspectorEnv = {
5
5
  Prod: "prod",
6
6
  Dev: "dev",
7
- Staging: "staging",
7
+ Staging: "staging"
8
8
  };
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AvoNetworkCallsHandler = void 0;
4
+ const AvoGuid_1 = require("./AvoGuid");
5
+ const AvoInspector_1 = require("./AvoInspector");
6
+ const AvoStreamId_1 = require("./AvoStreamId");
7
+ class AvoNetworkCallsHandler {
8
+ constructor(apiKey, envName, appName, appVersion, libVersion, publicEncryptionKey) {
9
+ this.samplingRate = 1.0;
10
+ this.sending = false;
11
+ this.apiKey = apiKey;
12
+ this.envName = envName;
13
+ this.appName = appName;
14
+ this.appVersion = appVersion;
15
+ this.libVersion = libVersion;
16
+ this.publicEncryptionKey = publicEncryptionKey;
17
+ }
18
+ callInspectorWithBatchBody(inEvents, onCompleted) {
19
+ if (this.sending) {
20
+ onCompleted(new Error("Batch sending cancelled because another batch sending is in progress. Your events will be sent with next batch."));
21
+ return;
22
+ }
23
+ const events = inEvents.filter((x) => x != null);
24
+ this.fixStreamIds(events);
25
+ if (events.length === 0) {
26
+ return;
27
+ }
28
+ if (this.shouldDropBySampling()) {
29
+ if (AvoInspector_1.AvoInspector.shouldLog) {
30
+ console.log("Avo Inspector: last event schema dropped due to sampling rate.");
31
+ }
32
+ return;
33
+ }
34
+ if (AvoInspector_1.AvoInspector.shouldLog) {
35
+ console.log("Avo Inspector: events", events);
36
+ events.forEach((event) => {
37
+ if (event.type === "sessionStarted") {
38
+ console.log("Avo Inspector: sending session started event.");
39
+ }
40
+ else if (event.type === "event") {
41
+ console.log("Avo Inspector: sending event " +
42
+ event.eventName +
43
+ " with schema " +
44
+ JSON.stringify(event.eventProperties));
45
+ }
46
+ });
47
+ }
48
+ this.sending = true;
49
+ this.callInspectorApi(events, (error) => {
50
+ this.sending = false;
51
+ onCompleted(error);
52
+ });
53
+ }
54
+ fixStreamIds(events) {
55
+ let knownStreamId = null;
56
+ events.forEach(function (event) {
57
+ if (event.streamId !== null &&
58
+ event.streamId !== undefined &&
59
+ event.streamId !== "unknown") {
60
+ knownStreamId = event.streamId;
61
+ }
62
+ });
63
+ events.forEach(function (event) {
64
+ if (event.streamId === "unknown") {
65
+ if (knownStreamId != null) {
66
+ event.streamId = knownStreamId;
67
+ }
68
+ else {
69
+ event.streamId = AvoStreamId_1.AvoStreamId.streamId;
70
+ }
71
+ }
72
+ });
73
+ }
74
+ bodyForSessionStartedCall() {
75
+ const sessionBody = this.createBaseCallBody();
76
+ sessionBody.type = "sessionStarted";
77
+ return sessionBody;
78
+ }
79
+ bodyForEventSchemaCall(eventName, eventProperties, eventId, eventHash, eventSpecMetadata, validatedBranchId) {
80
+ const eventSchemaBody = this.createBaseCallBody();
81
+ eventSchemaBody.type = "event";
82
+ eventSchemaBody.eventName = eventName;
83
+ eventSchemaBody.eventProperties = eventProperties;
84
+ if (eventId != null) {
85
+ eventSchemaBody.avoFunction = true;
86
+ eventSchemaBody.eventId = eventId;
87
+ eventSchemaBody.eventHash = eventHash;
88
+ }
89
+ else {
90
+ eventSchemaBody.avoFunction = false;
91
+ eventSchemaBody.eventId = null;
92
+ eventSchemaBody.eventHash = null;
93
+ }
94
+ // Set metadata on base body if provided
95
+ if (eventSpecMetadata) {
96
+ eventSchemaBody.eventSpecMetadata = eventSpecMetadata;
97
+ }
98
+ // Set validated branch ID if value validation was performed
99
+ if (validatedBranchId) {
100
+ eventSchemaBody.validatedBranchId = validatedBranchId;
101
+ }
102
+ return eventSchemaBody;
103
+ }
104
+ createBaseCallBody() {
105
+ const body = {
106
+ apiKey: this.apiKey,
107
+ appName: this.appName,
108
+ appVersion: this.appVersion,
109
+ libVersion: this.libVersion,
110
+ env: this.envName,
111
+ libPlatform: "web",
112
+ messageId: AvoGuid_1.default.newGuid(),
113
+ trackingId: "",
114
+ createdAt: new Date().toISOString(),
115
+ sessionId: "",
116
+ streamId: AvoStreamId_1.AvoStreamId.streamId,
117
+ samplingRate: this.samplingRate
118
+ };
119
+ if (this.publicEncryptionKey) {
120
+ body.publicEncryptionKey = this.publicEncryptionKey;
121
+ }
122
+ return body;
123
+ }
124
+ /**
125
+ * Calls Inspector API immediately with a single event (bypasses batching).
126
+ * Used when event spec validation is available.
127
+ * Note: Does not drop due to sampling - validated events are always sent.
128
+ */
129
+ callInspectorImmediately(eventBody, onCompleted) {
130
+ // Fix stream ID if needed
131
+ if (eventBody.streamId === "unknown") {
132
+ eventBody.streamId = AvoStreamId_1.AvoStreamId.streamId;
133
+ }
134
+ if (AvoInspector_1.AvoInspector.shouldLog) {
135
+ console.log("Avo Inspector: calling inspector immediately (with validation)", eventBody.eventName);
136
+ console.log("Avo Inspector: event body", eventBody);
137
+ }
138
+ this.callInspectorApi([eventBody], onCompleted);
139
+ }
140
+ /**
141
+ * Check if event should be dropped based on sampling rate.
142
+ */
143
+ shouldDropBySampling() {
144
+ return Math.random() > this.samplingRate;
145
+ }
146
+ /**
147
+ * Core Inspector API call logic shared by batch and immediate calls.
148
+ */
149
+ callInspectorApi(events, onCompleted) {
150
+ const xmlhttp = new XMLHttpRequest();
151
+ xmlhttp.open("POST", AvoNetworkCallsHandler.trackingEndpoint, true);
152
+ xmlhttp.setRequestHeader("Content-Type", "text/plain");
153
+ xmlhttp.timeout = AvoInspector_1.AvoInspector.networkTimeout;
154
+ xmlhttp.send(JSON.stringify(events));
155
+ xmlhttp.onload = () => {
156
+ if (xmlhttp.status !== 200) {
157
+ onCompleted(new Error(`Error ${xmlhttp.status}: ${xmlhttp.statusText}`));
158
+ }
159
+ else {
160
+ let response;
161
+ try {
162
+ response = JSON.parse(xmlhttp.response);
163
+ }
164
+ catch (e) {
165
+ onCompleted(new Error(`Failed to parse response: ${e instanceof Error ? e.message : String(e)}`));
166
+ return;
167
+ }
168
+ if (response != null &&
169
+ typeof response.samplingRate === "number" &&
170
+ !isNaN(response.samplingRate)) {
171
+ this.samplingRate = response.samplingRate;
172
+ }
173
+ onCompleted(null);
174
+ }
175
+ };
176
+ xmlhttp.onerror = () => {
177
+ onCompleted(new Error("Request failed"));
178
+ };
179
+ xmlhttp.ontimeout = () => {
180
+ onCompleted(new Error("Request timed out"));
181
+ };
182
+ }
183
+ }
184
+ exports.AvoNetworkCallsHandler = AvoNetworkCallsHandler;
185
+ AvoNetworkCallsHandler.trackingEndpoint = "https://api.avo.app/inspector/v1/track";