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.
Files changed (52) hide show
  1. package/README.md +6 -13
  2. package/dist/AvoBatcher.js +52 -44
  3. package/dist/AvoDeduplicator.js +46 -45
  4. package/dist/AvoEncryption.js +147 -253
  5. package/dist/AvoInspector.js +295 -334
  6. package/dist/AvoNetworkCallsHandler.d.ts +6 -0
  7. package/dist/AvoNetworkCallsHandler.js +180 -136
  8. package/dist/AvoSchemaParser.js +34 -71
  9. package/dist/AvoStreamId.js +67 -129
  10. package/dist/__mocks__/xhr.js +14 -0
  11. package/dist/__mocks__/xhrError.js +14 -0
  12. package/dist/__tests__/AvoEncryption.d.ts +1 -0
  13. package/dist/__tests__/AvoEncryption.js +272 -0
  14. package/dist/__tests__/AvoEncryptionIntegration.d.ts +1 -0
  15. package/dist/__tests__/AvoEncryptionIntegration.js +191 -0
  16. package/dist/__tests__/AvoEventSpecFetcher.d.ts +1 -0
  17. package/dist/__tests__/AvoEventSpecFetcher.js +377 -0
  18. package/dist/__tests__/AvoInspector.d.ts +1 -0
  19. package/dist/__tests__/AvoInspector.js +335 -0
  20. package/dist/__tests__/Batcher.d.ts +1 -0
  21. package/dist/__tests__/Batcher.js +214 -0
  22. package/dist/__tests__/BatcherPublic.d.ts +1 -0
  23. package/dist/__tests__/BatcherPublic.js +139 -0
  24. package/dist/__tests__/Deduplicator.d.ts +1 -0
  25. package/dist/__tests__/Deduplicator.js +190 -0
  26. package/dist/__tests__/EventSpecCache.d.ts +1 -0
  27. package/dist/__tests__/EventSpecCache.js +143 -0
  28. package/dist/__tests__/EventValidator.d.ts +1 -0
  29. package/dist/__tests__/EventValidator.js +543 -0
  30. package/dist/__tests__/InstallationId.d.ts +1 -0
  31. package/dist/__tests__/InstallationId.js +60 -0
  32. package/dist/__tests__/NetworkCallsHandler.d.ts +1 -0
  33. package/dist/__tests__/NetworkCallsHandler.js +222 -0
  34. package/dist/__tests__/NetworkCallsHandlerGzip_test.d.ts +1 -0
  35. package/dist/__tests__/NetworkCallsHandlerModelA.d.ts +1 -0
  36. package/dist/__tests__/NetworkCallsHandlerModelA.js +206 -0
  37. package/dist/__tests__/Parsing.d.ts +1 -0
  38. package/dist/__tests__/Parsing.js +115 -0
  39. package/dist/__tests__/Sessions.d.ts +1 -0
  40. package/dist/__tests__/Sessions.js +10 -0
  41. package/dist/__tests__/Storage.d.ts +1 -0
  42. package/dist/__tests__/Storage.js +173 -0
  43. package/dist/__tests__/StreamId.d.ts +1 -0
  44. package/dist/__tests__/StreamId.js +153 -0
  45. package/dist/__tests__/constants.js +48 -0
  46. package/dist/eventSpec/AvoEventSpecCache.js +94 -82
  47. package/dist/eventSpec/AvoEventSpecFetcher.js +200 -117
  48. package/dist/eventSpec/EventValidator.js +460 -304
  49. package/dist/index.js +1 -1
  50. package/dist/lite/AvoNetworkCallsHandlerLite.d.ts +6 -0
  51. package/dist/lite/AvoNetworkCallsHandlerLite.js +110 -1
  52. package/package.json +2 -2
@@ -1,144 +1,82 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AvoStreamId = void 0;
4
- const AvoGuid_1 = require("./AvoGuid");
5
- const AvoInspector_1 = require("./AvoInspector");
6
- /**
7
- * AvoStreamId manages an ephemeral stream identifier.
8
- *
9
- * The stream ID is generated and stored persistently, but resets after:
10
- * - 4 hours of total age AND
11
- * - 2 hours of idle time (no activity)
12
- *
13
- * This ensures the ID is truly anonymous by not persisting indefinitely,
14
- * while still maintaining session continuity during active usage.
15
- */
16
- class AvoStreamId {
17
- /**
18
- * Get the stream ID. If it doesn't exist or has expired, generates a new one.
19
- * Returns "unknown" if storage is not initialized.
20
- */
21
- static get streamId() {
22
- // Return cached value if available and not expired
23
- if (AvoStreamId._streamId !== null && !AvoStreamId.shouldReset()) {
24
- AvoStreamId.updateLastActivity();
25
- return AvoStreamId._streamId;
26
- }
27
- if (!AvoInspector_1.AvoInspector.avoStorage.isInitialized()) {
28
- return "unknown";
29
- }
30
- // Load from storage
31
- AvoStreamId.loadFromStorage();
32
- // Check if we should reset
33
- if (AvoStreamId._streamId !== null && AvoStreamId.shouldReset()) {
34
- return AvoStreamId.generateAndStoreNew();
35
- }
36
- // If no ID exists, generate new one
37
- if (AvoStreamId._streamId === null) {
38
- return AvoStreamId.generateAndStoreNew();
39
- }
40
- // Update last activity and return existing
41
- AvoStreamId.updateLastActivity();
42
- return AvoStreamId._streamId;
4
+ var AvoGuid_1 = require("./AvoGuid");
5
+ var AvoInspector_1 = require("./AvoInspector");
6
+ var AvoStreamId = /** @class */ (function () {
7
+ function AvoStreamId() {
43
8
  }
44
9
  /**
45
- * Check if the stream ID should be reset based on age and idle time.
10
+ * Returns the persistent anonymous ID (Model A).
11
+ * Generates a UUID once and persists it via AsyncStorage.
12
+ * Never resets based on time.
13
+ * Concurrent calls before cache is populated all return the same UUID.
46
14
  */
47
- static shouldReset() {
48
- if (AvoStreamId._createdAt === null || AvoStreamId._lastActivityAt === null) {
49
- return false;
15
+ AvoStreamId.initialize = function () {
16
+ // Return cached value immediately if available
17
+ if (AvoStreamId._anonymousId !== null) {
18
+ return Promise.resolve(AvoStreamId._anonymousId);
50
19
  }
51
- const now = Date.now();
52
- const age = now - AvoStreamId._createdAt;
53
- const idleTime = now - AvoStreamId._lastActivityAt;
54
- // Reset if older than 4 hours AND idle for 2+ hours
55
- return age > AvoStreamId.MAX_AGE_MS && idleTime > AvoStreamId.IDLE_THRESHOLD_MS;
56
- }
57
- /**
58
- * Load stream ID and timestamps from storage into cache.
59
- */
60
- static loadFromStorage() {
61
- try {
62
- const storedId = AvoInspector_1.AvoInspector.avoStorage.getItem(AvoStreamId.STREAM_ID_KEY);
63
- const createdAt = AvoInspector_1.AvoInspector.avoStorage.getItem(AvoStreamId.CREATED_AT_KEY);
64
- const lastActivityAt = AvoInspector_1.AvoInspector.avoStorage.getItem(AvoStreamId.LAST_ACTIVITY_KEY);
65
- if (storedId !== null && storedId !== undefined) {
66
- AvoStreamId._streamId = storedId;
67
- AvoStreamId._createdAt = createdAt ?? Date.now();
68
- AvoStreamId._lastActivityAt = lastActivityAt ?? Date.now();
69
- }
70
- }
71
- catch (e) {
72
- console.error("Avo Inspector: Error reading stream ID from storage. Please report to support@avo.app.", e);
73
- }
74
- }
75
- /**
76
- * Generate a new stream ID and store it with timestamps.
77
- */
78
- static generateAndStoreNew() {
79
- const now = Date.now();
80
- AvoStreamId._streamId = AvoGuid_1.default.newGuid();
81
- AvoStreamId._createdAt = now;
82
- AvoStreamId._lastActivityAt = now;
83
- try {
84
- AvoInspector_1.AvoInspector.avoStorage.setItem(AvoStreamId.STREAM_ID_KEY, AvoStreamId._streamId);
85
- AvoInspector_1.AvoInspector.avoStorage.setItem(AvoStreamId.CREATED_AT_KEY, AvoStreamId._createdAt);
86
- AvoInspector_1.AvoInspector.avoStorage.setItem(AvoStreamId.LAST_ACTIVITY_KEY, AvoStreamId._lastActivityAt);
20
+ // Return the in-flight promise if initialization is already underway
21
+ if (AvoStreamId._initializationPromise !== null) {
22
+ return AvoStreamId._initializationPromise;
87
23
  }
88
- catch (e) {
89
- console.error("Avo Inspector: Error saving stream ID to storage. Please report to support@avo.app.", e);
24
+ // Guard: if storage isn't initialized yet, fall back to a fresh GUID
25
+ if (!AvoInspector_1.AvoInspector.avoStorage) {
26
+ var fallbackId = AvoGuid_1.default.newGuid();
27
+ AvoStreamId._anonymousId = fallbackId;
28
+ return Promise.resolve(fallbackId);
90
29
  }
91
- return AvoStreamId._streamId;
92
- }
30
+ // Start initialization and cache the promise to handle concurrent calls
31
+ var storagePromise = AvoInspector_1.AvoInspector.avoStorage.getItemAsync(AvoStreamId.cacheKey);
32
+ var resolvedPromise = storagePromise && typeof storagePromise.then === "function"
33
+ ? storagePromise
34
+ : Promise.resolve(null);
35
+ AvoStreamId._initializationPromise = resolvedPromise.then(function (maybeId) {
36
+ if (maybeId !== null && maybeId !== undefined) {
37
+ AvoStreamId._anonymousId = maybeId;
38
+ }
39
+ else {
40
+ AvoStreamId._anonymousId = AvoGuid_1.default.newGuid();
41
+ AvoInspector_1.AvoInspector.avoStorage.setItem(AvoStreamId.cacheKey, AvoStreamId._anonymousId);
42
+ }
43
+ return AvoStreamId._anonymousId;
44
+ }).catch(function (error) {
45
+ // Reset so callers can retry on next call
46
+ AvoStreamId._initializationPromise = null;
47
+ // Fall back to a fresh GUID
48
+ var fallbackId = AvoGuid_1.default.newGuid();
49
+ AvoStreamId._anonymousId = fallbackId;
50
+ return fallbackId;
51
+ });
52
+ return AvoStreamId._initializationPromise;
53
+ };
93
54
  /**
94
- * Update the last activity timestamp.
55
+ * Sets a custom anonymous ID. Persists to storage.
95
56
  */
96
- static updateLastActivity() {
97
- const now = Date.now();
98
- AvoStreamId._lastActivityAt = now;
99
- try {
100
- AvoInspector_1.AvoInspector.avoStorage.setItem(AvoStreamId.LAST_ACTIVITY_KEY, now);
101
- }
102
- catch (e) {
103
- // Silently fail - not critical
57
+ AvoStreamId.setAnonymousId = function (id) {
58
+ AvoStreamId._anonymousId = id;
59
+ AvoStreamId._initializationPromise = null;
60
+ if (AvoInspector_1.AvoInspector.avoStorage) {
61
+ AvoInspector_1.AvoInspector.avoStorage.setItem(AvoStreamId.cacheKey, id);
104
62
  }
105
- }
106
- /**
107
- * The storage key used to persist the stream ID.
108
- */
109
- static get storageKey() {
110
- return AvoStreamId.STREAM_ID_KEY;
111
- }
63
+ };
112
64
  /**
113
- * The storage key for created at timestamp.
65
+ * Clears the cached anonymous ID. The next access will reload from storage.
114
66
  */
115
- static get createdAtKey() {
116
- return AvoStreamId.CREATED_AT_KEY;
117
- }
118
- /**
119
- * The storage key for last activity timestamp.
120
- */
121
- static get lastActivityKey() {
122
- return AvoStreamId.LAST_ACTIVITY_KEY;
123
- }
124
- /**
125
- * Clear the cached stream ID. The next access will reload from storage.
126
- * This is primarily useful for testing.
127
- */
128
- static clearCache() {
129
- AvoStreamId._streamId = null;
130
- AvoStreamId._createdAt = null;
131
- AvoStreamId._lastActivityAt = null;
132
- }
133
- }
67
+ AvoStreamId.clearCache = function () {
68
+ AvoStreamId._anonymousId = null;
69
+ AvoStreamId._initializationPromise = null;
70
+ };
71
+ Object.defineProperty(AvoStreamId, "cacheKey", {
72
+ get: function () {
73
+ return "AvoInspectorAnonymousId";
74
+ },
75
+ enumerable: false,
76
+ configurable: true
77
+ });
78
+ AvoStreamId._anonymousId = null;
79
+ AvoStreamId._initializationPromise = null;
80
+ return AvoStreamId;
81
+ }());
134
82
  exports.AvoStreamId = AvoStreamId;
135
- AvoStreamId._streamId = null;
136
- AvoStreamId._createdAt = null;
137
- AvoStreamId._lastActivityAt = null;
138
- // Ephemeral reset thresholds
139
- AvoStreamId.MAX_AGE_MS = 4 * 60 * 60 * 1000; // 4 hours
140
- AvoStreamId.IDLE_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
141
- // Storage keys
142
- AvoStreamId.STREAM_ID_KEY = "AvoInspectorStreamId";
143
- AvoStreamId.CREATED_AT_KEY = "AvoInspectorStreamIdCreatedAt";
144
- AvoStreamId.LAST_ACTIVITY_KEY = "AvoInspectorStreamIdLastActivityAt";
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var xhrMock = {
4
+ open: jest.fn(),
5
+ send: jest.fn(),
6
+ setRequestHeader: jest.fn(),
7
+ onload: jest.fn(),
8
+ onerror: jest.fn(),
9
+ ontimeout: jest.fn(),
10
+ status: 200,
11
+ response: "{}",
12
+ };
13
+ window.XMLHttpRequest = jest.fn(function () { return xhrMock; });
14
+ exports.default = xhrMock;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var xhrErrorMock = {
4
+ open: jest.fn(),
5
+ send: jest.fn(),
6
+ setRequestHeader: jest.fn(),
7
+ onload: jest.fn(),
8
+ onerror: jest.fn(),
9
+ ontimeout: jest.fn(),
10
+ status: 400,
11
+ statusText: "Bad Request",
12
+ };
13
+ window.XMLHttpRequest = jest.fn(function () { return xhrErrorMock; });
14
+ exports.default = xhrErrorMock;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Tests for AvoEncryption (React Native - @noble/ciphers based, fully synchronous).
5
+ *
6
+ * Covers:
7
+ * - shouldEncrypt() truth table
8
+ * - Wire format structural tests
9
+ * - Cross-SDK interop (TypeScript mirror of Java EncryptionInteropTestUtil)
10
+ * - Encryption failure handling
11
+ * - List-type property omission
12
+ * - publicEncryptionKey in base body
13
+ * - Prod negative test
14
+ */
15
+ var p256_1 = require("@noble/curves/p256");
16
+ var aes_1 = require("@noble/ciphers/aes");
17
+ var sha256_1 = require("@noble/hashes/sha256");
18
+ var AvoEncryption_1 = require("../AvoEncryption");
19
+ // =========================================================================
20
+ // Helper: TypeScript mirror of Java EncryptionInteropTestUtil.decrypt
21
+ // =========================================================================
22
+ function hexToBytes(hex) {
23
+ var bytes = new Uint8Array(hex.length / 2);
24
+ for (var i = 0; i < hex.length; i += 2) {
25
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
26
+ }
27
+ return bytes;
28
+ }
29
+ function bytesToHex(bytes) {
30
+ return Array.from(bytes)
31
+ .map(function (b) { return b.toString(16).padStart(2, "0"); })
32
+ .join("");
33
+ }
34
+ /**
35
+ * Reference decryptor mirroring Java EncryptionInteropTestUtil.decrypt.
36
+ * Uses @noble/curves + @noble/ciphers (same libs, but acts as independent verification).
37
+ */
38
+ function referenceDecrypt(base64Encrypted, privateKeyHex) {
39
+ var data = Buffer.from(base64Encrypted, "base64");
40
+ // Minimum length: 1 (version) + 65 (ephemeral pub key) + 16 (IV) + 16 (auth tag) + 1 (min ciphertext)
41
+ if (data.length < 99) {
42
+ throw new Error("Encrypted data too short: expected at least 99 bytes, got ".concat(data.length));
43
+ }
44
+ // Step 2: Assert version byte
45
+ if (data[0] !== 0x00) {
46
+ throw new Error("Unsupported version byte: expected 0x00, got 0x".concat(data[0].toString(16).padStart(2, "0")));
47
+ }
48
+ // Step 3: Extract ephemeral public key bytes [1..65] (65 bytes, uncompressed EC point)
49
+ var ephemeralPubKeyBytes = new Uint8Array(data.slice(1, 66));
50
+ // Step 4: Extract IV [66..81] (16 bytes)
51
+ var iv = new Uint8Array(data.slice(66, 82));
52
+ // Step 5: Extract auth tag [82..97] (16 bytes)
53
+ var authTag = new Uint8Array(data.slice(82, 98));
54
+ // Step 6: Extract ciphertext [98..N]
55
+ var ciphertext = new Uint8Array(data.slice(98));
56
+ // Step 7-8: ECDH with recipient private key -> sharedSecret
57
+ var privateKeyBytes = hexToBytes(privateKeyHex);
58
+ var sharedSecretPoint = p256_1.p256.getSharedSecret(privateKeyBytes, ephemeralPubKeyBytes);
59
+ var sharedSecret = sharedSecretPoint.slice(-32); // X-coordinate
60
+ // Step 9: KDF: SHA-256(sharedSecret) -> aesKey
61
+ var aesKey = (0, sha256_1.sha256)(sharedSecret);
62
+ // Step 10: AES/GCM decrypt
63
+ // @noble/ciphers gcm expects ciphertext + authTag concatenated
64
+ var ciphertextWithTag = new Uint8Array(ciphertext.length + authTag.length);
65
+ ciphertextWithTag.set(ciphertext, 0);
66
+ ciphertextWithTag.set(authTag, ciphertext.length);
67
+ var aes = (0, aes_1.gcm)(aesKey, iv);
68
+ var plainBytes = aes.decrypt(ciphertextWithTag);
69
+ // Step 11: Return as UTF-8 string
70
+ return new TextDecoder().decode(plainBytes);
71
+ }
72
+ // =========================================================================
73
+ // Generate a test key pair
74
+ // =========================================================================
75
+ function generateTestKeyPair() {
76
+ var privateKeyBytes = p256_1.p256.utils.randomPrivateKey();
77
+ var publicKeyBytes = p256_1.p256.getPublicKey(privateKeyBytes, false);
78
+ return {
79
+ privateKey: bytesToHex(privateKeyBytes).padStart(64, "0"),
80
+ publicKey: bytesToHex(publicKeyBytes),
81
+ };
82
+ }
83
+ // =========================================================================
84
+ // Tests
85
+ // =========================================================================
86
+ describe("AvoEncryption (React Native - @noble/ciphers)", function () {
87
+ // -----------------------------------------------------------------------
88
+ // shouldEncrypt() truth table
89
+ // -----------------------------------------------------------------------
90
+ describe("shouldEncrypt()", function () {
91
+ test("dev + valid key = true", function () {
92
+ expect((0, AvoEncryption_1.shouldEncrypt)("dev", "04abcdef1234")).toBe(true);
93
+ });
94
+ test("staging + valid key = true", function () {
95
+ expect((0, AvoEncryption_1.shouldEncrypt)("staging", "04abcdef1234")).toBe(true);
96
+ });
97
+ test("prod + valid key = false", function () {
98
+ expect((0, AvoEncryption_1.shouldEncrypt)("prod", "04abcdef1234")).toBe(false);
99
+ });
100
+ test("dev + null key = false", function () {
101
+ expect((0, AvoEncryption_1.shouldEncrypt)("dev", null)).toBe(false);
102
+ });
103
+ test("dev + undefined key = false", function () {
104
+ expect((0, AvoEncryption_1.shouldEncrypt)("dev", undefined)).toBe(false);
105
+ });
106
+ test("dev + empty string key = false", function () {
107
+ expect((0, AvoEncryption_1.shouldEncrypt)("dev", "")).toBe(false);
108
+ });
109
+ test("dev + whitespace-only key = false", function () {
110
+ expect((0, AvoEncryption_1.shouldEncrypt)("dev", " ")).toBe(false);
111
+ });
112
+ });
113
+ // -----------------------------------------------------------------------
114
+ // Wire format structural test
115
+ // -----------------------------------------------------------------------
116
+ describe("Wire format", function () {
117
+ var keyPair = generateTestKeyPair();
118
+ test("base64Decode(output).length >= 99", function () {
119
+ var encrypted = (0, AvoEncryption_1.encryptValue)("hello world", keyPair.publicKey);
120
+ var decoded = Buffer.from(encrypted, "base64");
121
+ expect(decoded.length).toBeGreaterThanOrEqual(99);
122
+ });
123
+ test("output[0] == 0x00 (version byte)", function () {
124
+ var encrypted = (0, AvoEncryption_1.encryptValue)("hello world", keyPair.publicKey);
125
+ var decoded = Buffer.from(encrypted, "base64");
126
+ expect(decoded[0]).toBe(0x00);
127
+ });
128
+ test("output[1] == 0x04 (uncompressed point marker)", function () {
129
+ var encrypted = (0, AvoEncryption_1.encryptValue)("hello world", keyPair.publicKey);
130
+ var decoded = Buffer.from(encrypted, "base64");
131
+ expect(decoded[1]).toBe(0x04);
132
+ });
133
+ test("encryptValue is synchronous (returns string, not Promise)", function () {
134
+ var result = (0, AvoEncryption_1.encryptValue)("test", keyPair.publicKey);
135
+ // If it were async, result would be a Promise, not a string
136
+ expect(typeof result).toBe("string");
137
+ // Ensure it's NOT a Promise
138
+ expect(result).not.toBeInstanceOf(Promise);
139
+ });
140
+ test("different encryptions produce different output", function () {
141
+ var enc1 = (0, AvoEncryption_1.encryptValue)("same", keyPair.publicKey);
142
+ var enc2 = (0, AvoEncryption_1.encryptValue)("same", keyPair.publicKey);
143
+ expect(enc1).not.toBe(enc2);
144
+ });
145
+ });
146
+ // -----------------------------------------------------------------------
147
+ // Cross-SDK interop: RN-encrypted ciphertext decryptable by reference decryptor
148
+ // -----------------------------------------------------------------------
149
+ describe("Cross-SDK interop", function () {
150
+ var keyPair = generateTestKeyPair();
151
+ var standardPlaintexts = [
152
+ '"hello world"',
153
+ "42",
154
+ "3.14",
155
+ "true",
156
+ '"test string value"',
157
+ ];
158
+ standardPlaintexts.forEach(function (plaintext) {
159
+ test("RN encrypt -> reference decrypt: ".concat(plaintext), function () {
160
+ var encrypted = (0, AvoEncryption_1.encryptValue)(plaintext, keyPair.publicKey);
161
+ var decrypted = referenceDecrypt(encrypted, keyPair.privateKey);
162
+ expect(decrypted).toBe(plaintext);
163
+ });
164
+ });
165
+ test("round-trip with JSON-stringified object", function () {
166
+ var obj = { key: "value", number: 123 };
167
+ var jsonStr = JSON.stringify(obj);
168
+ var encrypted = (0, AvoEncryption_1.encryptValue)(jsonStr, keyPair.publicKey);
169
+ var decrypted = referenceDecrypt(encrypted, keyPair.privateKey);
170
+ expect(decrypted).toBe(jsonStr);
171
+ });
172
+ });
173
+ // -----------------------------------------------------------------------
174
+ // encryptEventProperties
175
+ // -----------------------------------------------------------------------
176
+ describe("encryptEventProperties()", function () {
177
+ var keyPair = generateTestKeyPair();
178
+ test("encrypts string property values", function () {
179
+ var properties = [
180
+ { propertyName: "name", propertyType: "string" },
181
+ ];
182
+ var eventProps = { name: "Alice" };
183
+ var result = (0, AvoEncryption_1.encryptEventProperties)(properties, eventProps, keyPair.publicKey);
184
+ expect(result).toHaveLength(1);
185
+ expect(result[0].propertyName).toBe("name");
186
+ expect(result[0].propertyType).toBe("string");
187
+ expect(result[0].encryptedPropertyValue).toBeDefined();
188
+ // Verify it decrypts correctly
189
+ var decrypted = referenceDecrypt(result[0].encryptedPropertyValue, keyPair.privateKey);
190
+ expect(JSON.parse(decrypted)).toBe("Alice");
191
+ });
192
+ test("encrypts int property values", function () {
193
+ var properties = [
194
+ { propertyName: "age", propertyType: "int" },
195
+ ];
196
+ var eventProps = { age: 42 };
197
+ var result = (0, AvoEncryption_1.encryptEventProperties)(properties, eventProps, keyPair.publicKey);
198
+ expect(result[0].encryptedPropertyValue).toBeDefined();
199
+ var decrypted = referenceDecrypt(result[0].encryptedPropertyValue, keyPair.privateKey);
200
+ expect(JSON.parse(decrypted)).toBe(42);
201
+ });
202
+ test("encrypts boolean property values", function () {
203
+ var properties = [
204
+ { propertyName: "active", propertyType: "boolean" },
205
+ ];
206
+ var eventProps = { active: true };
207
+ var result = (0, AvoEncryption_1.encryptEventProperties)(properties, eventProps, keyPair.publicKey);
208
+ expect(result[0].encryptedPropertyValue).toBeDefined();
209
+ var decrypted = referenceDecrypt(result[0].encryptedPropertyValue, keyPair.privateKey);
210
+ expect(JSON.parse(decrypted)).toBe(true);
211
+ });
212
+ test("omits list-type properties entirely", function () {
213
+ var properties = [
214
+ { propertyName: "tags", propertyType: "list", children: ["string"] },
215
+ { propertyName: "name", propertyType: "string" },
216
+ ];
217
+ var eventProps = { tags: ["a", "b"], name: "Alice" };
218
+ var result = (0, AvoEncryption_1.encryptEventProperties)(properties, eventProps, keyPair.publicKey);
219
+ // list property should be omitted entirely
220
+ expect(result).toHaveLength(1);
221
+ expect(result[0].propertyName).toBe("name");
222
+ });
223
+ test("encryption failure: console.warn, omit property, continue", function () {
224
+ var warnSpy = jest.spyOn(console, "warn").mockImplementation();
225
+ var properties = [
226
+ { propertyName: "secret", propertyType: "string" },
227
+ { propertyName: "name", propertyType: "string" },
228
+ ];
229
+ var eventProps = { secret: "value1", name: "value2" };
230
+ // Use an invalid public key to trigger encryption failure for the first property
231
+ // We can't easily make only one fail, so let's test with a completely invalid key
232
+ var result = (0, AvoEncryption_1.encryptEventProperties)(properties, eventProps, "invalid-key");
233
+ // Both should fail with invalid key, so both should be omitted
234
+ expect(result).toHaveLength(0);
235
+ expect(warnSpy).toHaveBeenCalledTimes(1);
236
+ // Check that warning message matches expected format
237
+ var calls = warnSpy.mock.calls;
238
+ expect(calls.some(function (call) {
239
+ return String(call[0]).includes("[Avo Inspector] Encryption failed:");
240
+ })).toBe(true);
241
+ warnSpy.mockRestore();
242
+ });
243
+ test("null property values are encrypted as null", function () {
244
+ var properties = [
245
+ { propertyName: "nullProp", propertyType: "null" },
246
+ ];
247
+ var eventProps = { nullProp: null };
248
+ var result = (0, AvoEncryption_1.encryptEventProperties)(properties, eventProps, keyPair.publicKey);
249
+ expect(result).toHaveLength(1);
250
+ expect(result[0].encryptedPropertyValue).toBeDefined();
251
+ var decrypted = referenceDecrypt(result[0].encryptedPropertyValue, keyPair.privateKey);
252
+ expect(JSON.parse(decrypted)).toBeNull();
253
+ });
254
+ test("object property values are encrypted", function () {
255
+ var properties = [
256
+ {
257
+ propertyName: "address",
258
+ propertyType: "object",
259
+ children: [
260
+ { propertyName: "street", propertyType: "string" },
261
+ ],
262
+ },
263
+ ];
264
+ var eventProps = { address: { street: "123 Main St" } };
265
+ var result = (0, AvoEncryption_1.encryptEventProperties)(properties, eventProps, keyPair.publicKey);
266
+ expect(result).toHaveLength(1);
267
+ expect(result[0].encryptedPropertyValue).toBeDefined();
268
+ var decrypted = referenceDecrypt(result[0].encryptedPropertyValue, keyPair.privateKey);
269
+ expect(JSON.parse(decrypted)).toEqual({ street: "123 Main St" });
270
+ });
271
+ });
272
+ });
@@ -0,0 +1 @@
1
+ export {};