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
package/README.md CHANGED
@@ -25,6 +25,38 @@ or
25
25
  yarn add avo-inspector
26
26
  ```
27
27
 
28
+ # Lite Build (Production-Optimized)
29
+
30
+ For size-sensitive deployments (GTM, script tags, etc.), use the lite entry point which excludes encryption and event spec validation — reducing the gzipped bundle from ~37 KB to ~7.5 KB:
31
+
32
+ ```javascript
33
+ import { AvoInspector, AvoInspectorEnv } from "avo-inspector/lite";
34
+
35
+ let inspector = new AvoInspector({
36
+ apiKey: "your api key",
37
+ env: AvoInspectorEnv.Prod,
38
+ version: "1.0.0",
39
+ });
40
+ ```
41
+
42
+ The lite build has the same tracking API as the full version — `trackSchemaFromEvent`, `trackSchema`, and `extractSchema` all work identically. The differences:
43
+
44
+ - No `publicEncryptionKey` constructor option (TypeScript will error if passed)
45
+ - No event spec validation (dev/staging feature)
46
+ - No property value encryption (dev/staging feature)
47
+ - No stream ID generation/persistence (dev/staging debugger feature)
48
+ - No event deduplication — **if you use both Avo Codegen and manual `trackSchemaFromEvent` calls for the same events, use the full build instead** to avoid sending duplicate schemas
49
+ - Works universally with any bundler or minifier — no flags or configuration needed
50
+
51
+ **Size comparison:**
52
+
53
+ | | Minified | Gzipped |
54
+ |---|---|---|
55
+ | Full (`avo-inspector`) | 129 KB | 37 KB |
56
+ | Lite (`avo-inspector/lite`) | 20 KB | 5.2 KB |
57
+
58
+ See `examples/lite-size-demos/` for working examples with terser, webpack, and rollup.
59
+
28
60
  # Initialization
29
61
 
30
62
  Obtain the API key at [Avo.app](https://www.avo.app/welcome)
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AvoBatcher = void 0;
4
+ const AvoInspector_1 = require("./AvoInspector");
5
+ class AvoBatcher {
6
+ constructor(networkCallsHandler) {
7
+ this.events = [];
8
+ this.networkCallsHandler = networkCallsHandler;
9
+ this.batchFlushAttemptTimestamp = Date.now();
10
+ AvoInspector_1.AvoInspector.avoStorage
11
+ .getItemAsync(AvoBatcher.cacheKey)
12
+ .then((savedEvents) => {
13
+ if (savedEvents !== null) {
14
+ const nonNullSavedEvents = savedEvents.filter((event) => event !== null);
15
+ this.events = this.events.concat(nonNullSavedEvents);
16
+ this.checkIfBatchNeedsToBeSent();
17
+ }
18
+ })
19
+ .catch((error) => {
20
+ console.error("Avo Inspector: error getting events from cache", error);
21
+ });
22
+ }
23
+ handleSessionStarted() {
24
+ this.events.push(this.networkCallsHandler.bodyForSessionStartedCall());
25
+ this.saveEvents();
26
+ this.checkIfBatchNeedsToBeSent();
27
+ }
28
+ handleTrackSchema(eventName, schema, eventId, eventHash, eventSpecMetadata) {
29
+ this.events.push(this.networkCallsHandler.bodyForEventSchemaCall(eventName, schema, eventId, eventHash, eventSpecMetadata));
30
+ this.saveEvents();
31
+ if (AvoInspector_1.AvoInspector.shouldLog) {
32
+ console.log("Avo Inspector: saved event " +
33
+ eventName +
34
+ " with schema " +
35
+ JSON.stringify(schema));
36
+ }
37
+ this.checkIfBatchNeedsToBeSent();
38
+ }
39
+ checkIfBatchNeedsToBeSent() {
40
+ const batchSize = this.events.length;
41
+ const now = Date.now();
42
+ const timeSinceLastFlushAttempt = now - this.batchFlushAttemptTimestamp;
43
+ const sendBySize = batchSize % AvoInspector_1.AvoInspector.batchSize == 0;
44
+ const sendByTime = timeSinceLastFlushAttempt >= AvoInspector_1.AvoInspector.batchFlushSeconds * 1000;
45
+ const avoBatcher = this;
46
+ if (sendBySize || sendByTime) {
47
+ this.batchFlushAttemptTimestamp = now;
48
+ const sendingEvents = avoBatcher.events;
49
+ avoBatcher.events = [];
50
+ this.networkCallsHandler.callInspectorWithBatchBody(sendingEvents, function (error) {
51
+ if (error != null) {
52
+ avoBatcher.events = avoBatcher.events.concat(sendingEvents);
53
+ if (AvoInspector_1.AvoInspector.shouldLog) {
54
+ console.log("Avo Inspector: batch sending failed: " +
55
+ error.message +
56
+ ". We will attempt to send your schemas with next batch");
57
+ }
58
+ }
59
+ else {
60
+ if (AvoInspector_1.AvoInspector.shouldLog) {
61
+ console.log("Avo Inspector: batch sent successfully.");
62
+ }
63
+ }
64
+ avoBatcher.saveEvents();
65
+ });
66
+ }
67
+ }
68
+ saveEvents() {
69
+ if (this.events.length > 1000) {
70
+ const extraElements = this.events.length - 1000;
71
+ this.events.splice(0, extraElements);
72
+ }
73
+ AvoInspector_1.AvoInspector.avoStorage.setItem(AvoBatcher.cacheKey, this.events);
74
+ }
75
+ static get cacheKey() {
76
+ return "AvoInspectorEvents";
77
+ }
78
+ }
79
+ exports.AvoBatcher = AvoBatcher;
@@ -1,10 +1,18 @@
1
1
  export declare class AvoDeduplicator {
2
- avoFunctionsEvents: Record<number, string>;
3
- manualEvents: Record<number, string>;
4
- avoFunctionsEventsParams: Record<string, Record<string, any>>;
5
- manualEventsParams: Record<string, Record<string, any>>;
2
+ private _nextId;
3
+ avoFunctionsEvents: Record<string, string>;
4
+ manualEvents: Record<string, string>;
5
+ avoFunctionsEventsParams: Record<string, {
6
+ eventName: string;
7
+ params: Record<string, any>;
8
+ }>;
9
+ manualEventsParams: Record<string, {
10
+ eventName: string;
11
+ params: Record<string, any>;
12
+ }>;
6
13
  shouldRegisterEvent(eventName: string, params: Record<string, any>, fromAvoFunction: boolean): boolean;
7
14
  private hasSameEventAs;
15
+ private removeEntriesByName;
8
16
  private lookForEventIn;
9
17
  hasSeenEventParams(params: Record<string, any>, checkInAvoFunctions: boolean): boolean;
10
18
  private lookForEventParamsIn;
@@ -1,16 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AvoDeduplicator = void 0;
4
- var AvoSchemaParser_1 = require("./AvoSchemaParser");
5
- var utils_1 = require("./utils");
6
- var AvoDeduplicator = /** @class */ (function () {
7
- function AvoDeduplicator() {
4
+ const AvoSchemaParser_1 = require("./AvoSchemaParser");
5
+ const utils_1 = require("./utils");
6
+ class AvoDeduplicator {
7
+ constructor() {
8
8
  this.avoFunctionsEvents = {};
9
9
  this.manualEvents = {};
10
10
  this.avoFunctionsEventsParams = {};
11
11
  this.manualEventsParams = {};
12
12
  }
13
- AvoDeduplicator.prototype.shouldRegisterEvent = function (eventName, params, fromAvoFunction) {
13
+ shouldRegisterEvent(eventName, params, fromAvoFunction) {
14
14
  this.clearOldEvents();
15
15
  if (fromAvoFunction) {
16
16
  this.avoFunctionsEvents[Date.now()] = eventName;
@@ -20,11 +20,11 @@ var AvoDeduplicator = /** @class */ (function () {
20
20
  this.manualEvents[Date.now()] = eventName;
21
21
  this.manualEventsParams[eventName] = params;
22
22
  }
23
- var checkInAvoFunctions = !fromAvoFunction;
23
+ const checkInAvoFunctions = !fromAvoFunction;
24
24
  return !this.hasSameEventAs(eventName, params, checkInAvoFunctions);
25
- };
26
- AvoDeduplicator.prototype.hasSameEventAs = function (eventName, params, checkInAvoFunctions) {
27
- var result = false;
25
+ }
26
+ hasSameEventAs(eventName, params, checkInAvoFunctions) {
27
+ let result = false;
28
28
  if (checkInAvoFunctions) {
29
29
  if (this.lookForEventIn(eventName, params, this.avoFunctionsEventsParams)) {
30
30
  result = true;
@@ -40,12 +40,12 @@ var AvoDeduplicator = /** @class */ (function () {
40
40
  delete this.manualEventsParams[eventName];
41
41
  }
42
42
  return result;
43
- };
44
- AvoDeduplicator.prototype.lookForEventIn = function (eventName, params, eventsStorage) {
45
- for (var otherEventName in eventsStorage) {
43
+ }
44
+ lookForEventIn(eventName, params, eventsStorage) {
45
+ for (const otherEventName in eventsStorage) {
46
46
  if (eventsStorage.hasOwnProperty(otherEventName)) {
47
47
  if (otherEventName === eventName) {
48
- var otherParams = eventsStorage[eventName];
48
+ const otherParams = eventsStorage[eventName];
49
49
  if (otherParams && (0, utils_1.deepEquals)(params, otherParams)) {
50
50
  return true;
51
51
  }
@@ -53,9 +53,9 @@ var AvoDeduplicator = /** @class */ (function () {
53
53
  }
54
54
  }
55
55
  return false;
56
- };
57
- AvoDeduplicator.prototype.hasSeenEventParams = function (params, checkInAvoFunctions) {
58
- var result = false;
56
+ }
57
+ hasSeenEventParams(params, checkInAvoFunctions) {
58
+ let result = false;
59
59
  if (checkInAvoFunctions) {
60
60
  if (this.lookForEventParamsIn(params, this.avoFunctionsEventsParams)) {
61
61
  result = true;
@@ -67,37 +67,37 @@ var AvoDeduplicator = /** @class */ (function () {
67
67
  }
68
68
  }
69
69
  return result;
70
- };
71
- AvoDeduplicator.prototype.lookForEventParamsIn = function (params, eventsStorage) {
72
- for (var otherEventName in eventsStorage) {
70
+ }
71
+ lookForEventParamsIn(params, eventsStorage) {
72
+ for (const otherEventName in eventsStorage) {
73
73
  if (eventsStorage.hasOwnProperty(otherEventName)) {
74
- var otherParams = eventsStorage[otherEventName];
74
+ const otherParams = eventsStorage[otherEventName];
75
75
  if (otherParams && (0, utils_1.deepEquals)(params, otherParams)) {
76
76
  return true;
77
77
  }
78
78
  }
79
79
  }
80
80
  return false;
81
- };
82
- AvoDeduplicator.prototype.shouldRegisterSchemaFromManually = function (eventName, eventSchema) {
81
+ }
82
+ async shouldRegisterSchemaFromManually(eventName, eventSchema) {
83
83
  this.clearOldEvents();
84
- return !this.hasSameShapeInAvoFunctionsAs(eventName, eventSchema);
85
- };
86
- AvoDeduplicator.prototype.hasSameShapeInAvoFunctionsAs = function (eventName, eventSchema) {
87
- var result = false;
88
- if (this.lookForEventSchemaIn(eventName, eventSchema, this.avoFunctionsEventsParams)) {
84
+ return !(await this.hasSameShapeInAvoFunctionsAs(eventName, eventSchema));
85
+ }
86
+ async hasSameShapeInAvoFunctionsAs(eventName, eventSchema) {
87
+ let result = false;
88
+ if (await this.lookForEventSchemaIn(eventName, eventSchema, this.avoFunctionsEventsParams)) {
89
89
  result = true;
90
90
  }
91
91
  if (result) {
92
92
  delete this.avoFunctionsEventsParams[eventName];
93
93
  }
94
94
  return result;
95
- };
96
- AvoDeduplicator.prototype.lookForEventSchemaIn = function (eventName, eventSchema, eventsStorage) {
97
- for (var otherEventName in eventsStorage) {
95
+ }
96
+ async lookForEventSchemaIn(eventName, eventSchema, eventsStorage) {
97
+ for (const otherEventName in eventsStorage) {
98
98
  if (eventsStorage.hasOwnProperty(otherEventName)) {
99
99
  if (otherEventName === eventName) {
100
- var otherSchema = AvoSchemaParser_1.AvoSchemaParser.extractSchema(eventsStorage[eventName]);
100
+ const otherSchema = await AvoSchemaParser_1.AvoSchemaParser.extractSchema(eventsStorage[eventName]);
101
101
  if (otherSchema && (0, utils_1.deepEquals)(eventSchema, otherSchema)) {
102
102
  return true;
103
103
  }
@@ -105,38 +105,37 @@ var AvoDeduplicator = /** @class */ (function () {
105
105
  }
106
106
  }
107
107
  return false;
108
- };
109
- AvoDeduplicator.prototype.clearOldEvents = function () {
110
- var now = Date.now();
111
- var msToConsiderOld = 300;
112
- for (var time in this.avoFunctionsEvents) {
108
+ }
109
+ clearOldEvents() {
110
+ const now = Date.now();
111
+ const msToConsiderOld = 300;
112
+ for (const time in this.avoFunctionsEvents) {
113
113
  if (this.avoFunctionsEvents.hasOwnProperty(time)) {
114
- var timestamp = Number(time) || 0;
114
+ const timestamp = Number(time) || 0;
115
115
  if (now - timestamp > msToConsiderOld) {
116
- var eventName = this.avoFunctionsEvents[time];
116
+ const eventName = this.avoFunctionsEvents[time];
117
117
  delete this.avoFunctionsEvents[time];
118
118
  delete this.avoFunctionsEventsParams[eventName];
119
119
  }
120
120
  }
121
121
  }
122
- for (var time in this.manualEvents) {
122
+ for (const time in this.manualEvents) {
123
123
  if (this.manualEvents.hasOwnProperty(time)) {
124
- var timestamp = Number(time) || 0;
124
+ const timestamp = Number(time) || 0;
125
125
  if (now - timestamp > msToConsiderOld) {
126
- var eventName = this.manualEvents[time];
126
+ const eventName = this.manualEvents[time];
127
127
  delete this.manualEvents[time];
128
128
  delete this.manualEventsParams[eventName];
129
129
  }
130
130
  }
131
131
  }
132
- };
132
+ }
133
133
  // used in tests
134
- AvoDeduplicator.prototype._clearEvents = function () {
134
+ _clearEvents() {
135
135
  this.avoFunctionsEvents = {};
136
136
  this.manualEvents = {};
137
137
  this.avoFunctionsEventsParams = {};
138
138
  this.manualEventsParams = {};
139
- };
140
- return AvoDeduplicator;
141
- }());
139
+ }
140
+ }
142
141
  exports.AvoDeduplicator = AvoDeduplicator;
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateKeyPair = generateKeyPair;
4
+ exports.encryptValue = encryptValue;
5
+ exports.decryptValue = decryptValue;
6
+ /* eslint-disable @typescript-eslint/no-explicit-any */
7
+ const p256_1 = require("@noble/curves/p256");
8
+ /**
9
+ * Converts bytes (Uint8Array) to hex string
10
+ */
11
+ function bytesToHex(bytes) {
12
+ return Array.from(bytes)
13
+ .map(b => b.toString(16).padStart(2, '0'))
14
+ .join('');
15
+ }
16
+ /**
17
+ * Converts hex string to bytes (Uint8Array)
18
+ */
19
+ function hexToBytes(hex) {
20
+ const bytes = new Uint8Array(hex.length / 2);
21
+ for (let i = 0; i < hex.length; i += 2) {
22
+ bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
23
+ }
24
+ return bytes;
25
+ }
26
+ // Get crypto API - works in both browser and Node.js environments
27
+ // In browser: window.crypto or globalThis.crypto
28
+ // In Node.js: global.crypto or globalThis.crypto (polyfilled in tests)
29
+ const getCrypto = () => {
30
+ // Try globalThis first (works in both Node.js and browsers)
31
+ if (typeof globalThis !== 'undefined' && globalThis.crypto) {
32
+ const crypto = globalThis.crypto;
33
+ if (crypto && crypto.subtle) {
34
+ return crypto;
35
+ }
36
+ }
37
+ // Try window (browser)
38
+ if (typeof window !== 'undefined' && window.crypto) {
39
+ const crypto = window.crypto;
40
+ if (crypto && crypto.subtle) {
41
+ return crypto;
42
+ }
43
+ }
44
+ // Try global (Node.js)
45
+ if (typeof global !== 'undefined' && global.crypto) {
46
+ const crypto = global.crypto;
47
+ if (crypto && crypto.subtle) {
48
+ return crypto;
49
+ }
50
+ }
51
+ // Fallback - should not happen in proper environments
52
+ const debugInfo = {
53
+ hasGlobalThis: typeof globalThis !== 'undefined',
54
+ hasGlobalThisCrypto: typeof globalThis !== 'undefined' && !!globalThis.crypto,
55
+ hasGlobalThisCryptoSubtle: typeof globalThis !== 'undefined' && !!globalThis.crypto && !!globalThis.crypto.subtle,
56
+ hasWindow: typeof window !== 'undefined',
57
+ hasWindowCrypto: typeof window !== 'undefined' && !!window.crypto,
58
+ hasGlobal: typeof global !== 'undefined',
59
+ hasGlobalCrypto: typeof global !== 'undefined' && !!global.crypto,
60
+ hasGlobalCryptoSubtle: typeof global !== 'undefined' && !!global.crypto && !!global.crypto.subtle
61
+ };
62
+ throw new Error('crypto.subtle not available. Debug: ' + JSON.stringify(debugInfo));
63
+ };
64
+ /**
65
+ * Generates a new ECC key pair for encryption/decryption.
66
+ * Uses P-256 (prime256v1 / NIST P-256) curve which is standard for Web Crypto API.
67
+ *
68
+ * @returns An object containing the private and public keys as hex strings
69
+ */
70
+ function generateKeyPair() {
71
+ // Generate a new random private key (32 bytes)
72
+ const privateKeyBytes = p256_1.p256.utils.randomPrivateKey();
73
+ // Get public key (uncompressed format: 65 bytes = 0x04 + 32-byte x + 32-byte y)
74
+ const publicKeyBytes = p256_1.p256.getPublicKey(privateKeyBytes, false);
75
+ // Convert to hex strings
76
+ const privateKey = bytesToHex(privateKeyBytes).padStart(64, '0');
77
+ const publicKey = bytesToHex(publicKeyBytes);
78
+ return {
79
+ privateKey,
80
+ publicKey
81
+ };
82
+ }
83
+ /**
84
+ * Derives a key from shared secret using SHA-256
85
+ */
86
+ async function deriveKey(sharedSecret) {
87
+ const cryptoAPI = getCrypto();
88
+ return await cryptoAPI.subtle.digest('SHA-256', sharedSecret);
89
+ }
90
+ /**
91
+ * Safely converts Uint8Array to base64 string.
92
+ * Uses Buffer in Node.js for efficiency, or chunked conversion in browsers
93
+ * to avoid call stack overflow with large arrays.
94
+ */
95
+ function uint8ArrayToBase64(bytes) {
96
+ // Check if we're in Node.js and Buffer is available
97
+ if (typeof Buffer !== 'undefined' && Buffer.from) {
98
+ return Buffer.from(bytes).toString('base64');
99
+ }
100
+ // Browser fallback: use chunked conversion to avoid call stack overflow
101
+ // Chunk size of 8192 (8k) is safe for String.fromCharCode.apply
102
+ const CHUNK_SIZE = 8192;
103
+ let binaryString = '';
104
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
105
+ const chunk = bytes.slice(i, i + CHUNK_SIZE);
106
+ // Convert chunk to array and use apply for this small chunk
107
+ binaryString += String.fromCharCode.apply(null, Array.from(chunk));
108
+ }
109
+ return btoa(binaryString);
110
+ }
111
+ /**
112
+ * Encrypts a value using ECC public key encryption (ECIES).
113
+ * The encrypted output can only be decrypted by the client using their private key.
114
+ * This ensures that Avo cannot decrypt the values on the backend.
115
+ *
116
+ * ECIES uses hybrid encryption (ECDH + AES-256-GCM) which provides:
117
+ * - No message size limitations
118
+ * - Fast encryption even for large values
119
+ * - Strong authentication via GCM
120
+ *
121
+ * SPECIFICATION (Standard Web Crypto Profile):
122
+ * 1. Curve: P-256 (prime256v1 / NIST P-256)
123
+ * 2. Key Derivation (KDF): SHA-256(SharedSecret)
124
+ * 3. Cipher: AES-256-GCM
125
+ * 4. Serialization: [Version(1b)] + [EphemeralPubKey(33 or 65b)] + [IV(16b)] + [AuthTag(16b)] + [Ciphertext]
126
+ * Version 0x00 = Standard Web Profile
127
+ * EphemeralPubKey: 0x04 (uncompressed) + 64 bytes = 65 bytes total, or compressed format (33 bytes)
128
+ *
129
+ * @param value - The value to encrypt (any type - will be JSON stringified)
130
+ * @param publicKey - The ECC public key in hex format provided by the client
131
+ * @returns Promise resolving to base64-encoded encrypted string that can only be decrypted with the private key
132
+ */
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ async function encryptValue(value, publicKey) {
135
+ try {
136
+ // Convert the value to a JSON string to support all types
137
+ // Note: JSON.stringify(undefined) returns undefined (not a string), so handle it explicitly
138
+ const stringValue = value === undefined ? 'null' : JSON.stringify(value);
139
+ if (stringValue === undefined) {
140
+ throw new Error('Cannot encrypt undefined value');
141
+ }
142
+ // 1. Prepare Public Key
143
+ // Ensure publicKey is a string
144
+ const publicKeyStr = typeof publicKey === 'string' ? publicKey : String(publicKey);
145
+ // Convert recipient's public key from hex to bytes
146
+ const recipientPublicKeyBytes = hexToBytes(publicKeyStr);
147
+ // 2. Generate Ephemeral Key Pair on P-256
148
+ const ephemeralPrivateKeyBytes = p256_1.p256.utils.randomPrivateKey();
149
+ const ephemeralPublicKeyBytes = p256_1.p256.getPublicKey(ephemeralPrivateKeyBytes, false); // Uncompressed format (65 bytes)
150
+ // 3. Derive Shared Secret (ECDH)
151
+ // getSharedSecret returns compressed point (33 bytes: 0x02/0x03 + X-coordinate)
152
+ // Extract X-coordinate (last 32 bytes) to match elliptic's derive().toArray("be", 32)
153
+ const sharedSecretPoint = p256_1.p256.getSharedSecret(ephemeralPrivateKeyBytes, recipientPublicKeyBytes);
154
+ const sharedSecret = sharedSecretPoint.slice(-32); // Extract X-coordinate (last 32 bytes)
155
+ // 4. Key Derivation (Hash with SHA-256)
156
+ const derivedKeyBuffer = await deriveKey(sharedSecret);
157
+ // 5. Import Key for Web Crypto AES-GCM
158
+ const cryptoAPI = getCrypto();
159
+ const keyMaterial = await cryptoAPI.subtle.importKey('raw', derivedKeyBuffer, { name: 'AES-GCM' }, false, ['encrypt']);
160
+ // 6. Generate random IV (16 bytes)
161
+ const iv = cryptoAPI.getRandomValues(new Uint8Array(16));
162
+ // 7. Encrypt using AES-256-GCM
163
+ // Convert string to Uint8Array
164
+ const plaintext = new TextEncoder().encode(stringValue);
165
+ const encryptedData = await cryptoAPI.subtle.encrypt({
166
+ name: 'AES-GCM',
167
+ iv,
168
+ tagLength: 128 // 16 bytes = 128 bits
169
+ }, keyMaterial, plaintext);
170
+ // Web Crypto API appends the auth tag to the ciphertext
171
+ // We need to extract it separately for our format
172
+ const encryptedArray = new Uint8Array(encryptedData);
173
+ const authTagLength = 16; // 128 bits = 16 bytes
174
+ const ciphertextLength = encryptedArray.length - authTagLength;
175
+ const ciphertext = encryptedArray.slice(0, ciphertextLength);
176
+ const authTag = encryptedArray.slice(ciphertextLength);
177
+ // 8. Serialize Output
178
+ // Format: [Version(1)] + [Ephemeral Public Key(65)] + [IV(16)] + [AuthTag(16)] + [Ciphertext]
179
+ const version = new Uint8Array([0x00]); // Version 0: Standard Web Profile
180
+ // Combine all parts
181
+ const resultLength = 1 + ephemeralPublicKeyBytes.length + 16 + 16 + ciphertext.length;
182
+ const result = new Uint8Array(resultLength);
183
+ let offset = 0;
184
+ result.set(version, offset);
185
+ offset += 1;
186
+ result.set(ephemeralPublicKeyBytes, offset);
187
+ offset += ephemeralPublicKeyBytes.length;
188
+ result.set(iv, offset);
189
+ offset += 16;
190
+ result.set(authTag, offset);
191
+ offset += 16;
192
+ result.set(ciphertext, offset);
193
+ // Convert to base64 using safe conversion that handles large arrays
194
+ return uint8ArrayToBase64(result);
195
+ }
196
+ catch (error) {
197
+ throw new Error(`Failed to encrypt value. Please check that the public key is valid. Error: ${error instanceof Error ? error.message : String(error)}`);
198
+ }
199
+ }
200
+ /**
201
+ * Decrypts a value that was encrypted with encryptValue.
202
+ *
203
+ * SPECIFICATION (Standard Web Crypto Profile):
204
+ * 1. Curve: P-256 (prime256v1 / NIST P-256)
205
+ * 2. Key Derivation (KDF): SHA-256(SharedSecret)
206
+ * 3. Cipher: AES-256-GCM
207
+ * 4. Deserialization: [Version(1b)] + [EphemeralPubKey(33 or 65b)] + [IV(16b)] + [AuthTag(16b)] + [Ciphertext]
208
+ *
209
+ * @param encryptedValue - The base64-encoded encrypted string
210
+ * @param privateKey - The ECC private key in hex format
211
+ * @returns Promise resolving to the original decrypted value (parsed from JSON)
212
+ */
213
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
214
+ async function decryptValue(encryptedValue, privateKey) {
215
+ try {
216
+ // Convert encrypted value from base64 to Uint8Array
217
+ const binaryString = atob(encryptedValue);
218
+ const encryptedBuffer = new Uint8Array(binaryString.length);
219
+ for (let i = 0; i < binaryString.length; i++) {
220
+ encryptedBuffer[i] = binaryString.charCodeAt(i);
221
+ }
222
+ // Minimum size: version(1) + pubkey(33 min) + iv(16) + authTag(16) + ciphertext(1 min) = 67 bytes
223
+ if (encryptedBuffer.length < 67) {
224
+ throw new Error('Invalid encrypted data: payload too short');
225
+ }
226
+ // Ensure privateKey is a string
227
+ const privateKeyStr = typeof privateKey === 'string' ? privateKey : String(privateKey);
228
+ // 1. Deserialize Input
229
+ // Format: [Version(1)] + [Ephemeral Public Key(33 or 65)] + [IV(16)] + [AuthTag(16)] + [Ciphertext]
230
+ let offset = 0;
231
+ const version = encryptedBuffer[offset];
232
+ offset += 1;
233
+ if (version !== 0x00) {
234
+ throw new Error(`Unsupported encryption version: ${version}`);
235
+ }
236
+ // Check header to determine public key size (0x02/0x03 = 33 bytes compressed, 0x04 = 65 bytes uncompressed)
237
+ const keyHeader = encryptedBuffer[offset];
238
+ const pubKeySize = keyHeader === 0x02 || keyHeader === 0x03 ? 33 : 65;
239
+ // Validate buffer has enough bytes for this key format
240
+ const minRequired = 1 + pubKeySize + 16 + 16 + 1;
241
+ if (encryptedBuffer.length < minRequired) {
242
+ throw new Error(`Invalid encrypted data: expected at least ${minRequired} bytes, got ${encryptedBuffer.length}`);
243
+ }
244
+ const ephemeralPublicKey = encryptedBuffer.slice(offset, offset + pubKeySize);
245
+ offset += pubKeySize;
246
+ const iv = encryptedBuffer.slice(offset, offset + 16); // IV (16 bytes)
247
+ offset += 16;
248
+ const authTag = encryptedBuffer.slice(offset, offset + 16); // Auth tag (16 bytes)
249
+ offset += 16;
250
+ const ciphertext = encryptedBuffer.slice(offset); // Remaining bytes are ciphertext
251
+ // 2. Prepare Private Key
252
+ const recipientPrivateKeyBytes = hexToBytes(privateKeyStr);
253
+ // 3. Derive Shared Secret (ECDH)
254
+ // getSharedSecret returns compressed point (33 bytes: 0x02/0x03 + X-coordinate)
255
+ // Extract X-coordinate (last 32 bytes) to match elliptic's derive().toArray("be", 32)
256
+ const sharedSecretPoint = p256_1.p256.getSharedSecret(recipientPrivateKeyBytes, ephemeralPublicKey);
257
+ const sharedSecret = sharedSecretPoint.slice(-32); // Extract X-coordinate (last 32 bytes)
258
+ // 4. Key Derivation (Hash with SHA-256)
259
+ const derivedKeyBuffer = await deriveKey(sharedSecret);
260
+ // 5. Import Key for Web Crypto AES-GCM
261
+ const cryptoAPI = getCrypto();
262
+ const keyMaterial = await cryptoAPI.subtle.importKey('raw', derivedKeyBuffer, { name: 'AES-GCM' }, false, ['decrypt']);
263
+ // 6. Decrypt using AES-256-GCM
264
+ // Web Crypto API expects the auth tag appended to ciphertext
265
+ const combinedLength = ciphertext.length + authTag.length;
266
+ const data = new Uint8Array(combinedLength);
267
+ data.set(ciphertext, 0);
268
+ data.set(authTag, ciphertext.length);
269
+ const decryptedBuffer = await cryptoAPI.subtle.decrypt({
270
+ name: 'AES-GCM',
271
+ iv,
272
+ tagLength: 128 // 16 bytes = 128 bits
273
+ }, keyMaterial, data);
274
+ // 7. Convert to string and parse JSON
275
+ const decoder = new TextDecoder();
276
+ const stringValue = decoder.decode(decryptedBuffer);
277
+ return JSON.parse(stringValue);
278
+ }
279
+ catch (error) {
280
+ throw new Error(`Failed to decrypt value. Please check that the private key is valid and matches the public key used for encryption. Error: ${error instanceof Error ? error.message : String(error)}`);
281
+ }
282
+ }
@@ -5,7 +5,8 @@ var AvoGuid = /** @class */ (function () {
5
5
  }
6
6
  AvoGuid.newGuid = function () {
7
7
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
8
- var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
8
+ var r = (Math.random() * 16) | 0;
9
+ var v = c === "x" ? r : (r & 0x3) | 0x8;
9
10
  return v.toString(16);
10
11
  });
11
12
  };