avo-inspector 3.2.0 → 3.2.3

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 (84) hide show
  1. package/README.md +10 -0
  2. package/dist/AvoBatcher.d.ts +9 -3
  3. package/dist/AvoInspector.d.ts +4 -0
  4. package/dist/AvoNetworkCallsHandler.d.ts +80 -5
  5. package/dist/__tests__/UnboundPublicMethods_test.d.ts +1 -0
  6. package/dist/__tests__/constants.d.ts +2 -1
  7. package/dist/__tests__/helpers/internalGateway.d.ts +23 -0
  8. package/dist/eventSpec/AvoEventSpecCache.d.ts +7 -4
  9. package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +12 -1
  10. package/dist/eventSpec/AvoEventSpecFetcher.d.ts +6 -1
  11. package/dist/index.js +1 -1
  12. package/dist/lite/AvoBatcherLite.d.ts +9 -3
  13. package/dist/lite/AvoBatcherLite.js +46 -7
  14. package/dist/lite/AvoInspectorLite.d.ts +2 -0
  15. package/dist/lite/AvoInspectorLite.js +95 -15
  16. package/dist/lite/AvoNetworkCallsHandlerLite.d.ts +79 -5
  17. package/dist/lite/AvoNetworkCallsHandlerLite.js +300 -20
  18. package/package.json +10 -1
  19. package/dist/AvoBatcher.js +0 -87
  20. package/dist/AvoDeduplicator.js +0 -142
  21. package/dist/AvoEncryption.js +0 -176
  22. package/dist/AvoInspector.js +0 -435
  23. package/dist/AvoNetworkCallsHandler.js +0 -229
  24. package/dist/AvoSchemaParser.js +0 -95
  25. package/dist/AvoStreamId.js +0 -82
  26. package/dist/__mocks__/xhr.js +0 -14
  27. package/dist/__mocks__/xhrError.js +0 -14
  28. package/dist/__tests__/AvoEncryption.js +0 -272
  29. package/dist/__tests__/AvoEncryptionIntegration.js +0 -191
  30. package/dist/__tests__/AvoEventSpecFetcher.js +0 -377
  31. package/dist/__tests__/AvoInspector.js +0 -335
  32. package/dist/__tests__/Batcher.js +0 -214
  33. package/dist/__tests__/BatcherPublic.js +0 -139
  34. package/dist/__tests__/Deduplicator.js +0 -190
  35. package/dist/__tests__/EventSpecCache.js +0 -143
  36. package/dist/__tests__/EventValidator.js +0 -543
  37. package/dist/__tests__/InstallationId.js +0 -60
  38. package/dist/__tests__/NetworkCallsHandler.js +0 -222
  39. package/dist/__tests__/NetworkCallsHandlerModelA.js +0 -206
  40. package/dist/__tests__/Parsing.js +0 -115
  41. package/dist/__tests__/Sessions.d.ts +0 -1
  42. package/dist/__tests__/Sessions.js +0 -10
  43. package/dist/__tests__/Storage.d.ts +0 -1
  44. package/dist/__tests__/Storage.js +0 -173
  45. package/dist/__tests__/StreamId.d.ts +0 -1
  46. package/dist/__tests__/StreamId.js +0 -153
  47. package/dist/__tests__/constants.js +0 -48
  48. package/dist/esm/AvoBatcher.js +0 -75
  49. package/dist/esm/AvoDeduplicator.js +0 -137
  50. package/dist/esm/AvoEncryption.js +0 -277
  51. package/dist/esm/AvoGuid.js +0 -9
  52. package/dist/esm/AvoInspector.js +0 -470
  53. package/dist/esm/AvoInspectorEnv.js +0 -5
  54. package/dist/esm/AvoNetworkCallsHandler.js +0 -181
  55. package/dist/esm/AvoSchemaParser.js +0 -128
  56. package/dist/esm/AvoStorage.js +0 -176
  57. package/dist/esm/AvoStreamId.js +0 -140
  58. package/dist/esm/eventSpec/AvoEventSpecCache.js +0 -165
  59. package/dist/esm/eventSpec/AvoEventSpecFetchTypes.js +0 -6
  60. package/dist/esm/eventSpec/AvoEventSpecFetcher.js +0 -203
  61. package/dist/esm/eventSpec/EventValidator.js +0 -687
  62. package/dist/esm/eventSpec/index.js +0 -4
  63. package/dist/esm/index.js +0 -3
  64. package/dist/esm/utils.js +0 -38
  65. package/dist/esm/version.js +0 -2
  66. package/dist/eventSpec/AvoEventSpecCache.js +0 -181
  67. package/dist/eventSpec/AvoEventSpecFetcher.js +0 -290
  68. package/dist/eventSpec/EventValidator.js +0 -846
  69. package/dist/eventSpec/index.js +0 -20
  70. package/dist/version.d.ts +0 -1
  71. package/dist/version.js +0 -5
  72. /package/dist/__tests__/{AvoEncryption.d.ts → ApiKeyHeaderInjection_test.d.ts} +0 -0
  73. /package/dist/__tests__/{AvoEncryptionIntegration.d.ts → BrowserBootstrap_test.d.ts} +0 -0
  74. /package/dist/__tests__/{AvoEventSpecFetcher.d.ts → EventSpecGatewayCoordinates_test.d.ts} +0 -0
  75. /package/dist/__tests__/{AvoInspector.d.ts → InFlightQueueGaps_test.d.ts} +0 -0
  76. /package/dist/__tests__/{Batcher.d.ts → InFlightQueue_test.d.ts} +0 -0
  77. /package/dist/__tests__/{BatcherPublic.d.ts → NetworkCallsHandlerLite_test.d.ts} +0 -0
  78. /package/dist/__tests__/{Deduplicator.d.ts → PublicSurface_test.d.ts} +0 -0
  79. /package/dist/__tests__/{EventSpecCache.d.ts → TrackOptionsIntegration_test.d.ts} +0 -0
  80. /package/dist/__tests__/{EventValidator.d.ts → TrackOptionsTransport_test.d.ts} +0 -0
  81. /package/dist/__tests__/{InstallationId.d.ts → TrackingHeaders_test.d.ts} +0 -0
  82. /package/dist/__tests__/{NetworkCallsHandler.d.ts → V1WireBaseline_test.d.ts} +0 -0
  83. /package/dist/__tests__/{NetworkCallsHandlerModelA.d.ts → V2QueuedAttribution_test.d.ts} +0 -0
  84. /package/dist/__tests__/{Parsing.d.ts → V2StopSending_test.d.ts} +0 -0
@@ -1,277 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { p256 } from '@noble/curves/p256';
3
- /**
4
- * Converts bytes (Uint8Array) to hex string
5
- */
6
- function bytesToHex(bytes) {
7
- return Array.from(bytes)
8
- .map(b => b.toString(16).padStart(2, '0'))
9
- .join('');
10
- }
11
- /**
12
- * Converts hex string to bytes (Uint8Array)
13
- */
14
- function hexToBytes(hex) {
15
- const bytes = new Uint8Array(hex.length / 2);
16
- for (let i = 0; i < hex.length; i += 2) {
17
- bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
18
- }
19
- return bytes;
20
- }
21
- // Get crypto API - works in both browser and Node.js environments
22
- // In browser: window.crypto or globalThis.crypto
23
- // In Node.js: global.crypto or globalThis.crypto (polyfilled in tests)
24
- const getCrypto = () => {
25
- // Try globalThis first (works in both Node.js and browsers)
26
- if (typeof globalThis !== 'undefined' && globalThis.crypto) {
27
- const crypto = globalThis.crypto;
28
- if (crypto && crypto.subtle) {
29
- return crypto;
30
- }
31
- }
32
- // Try window (browser)
33
- if (typeof window !== 'undefined' && window.crypto) {
34
- const crypto = window.crypto;
35
- if (crypto && crypto.subtle) {
36
- return crypto;
37
- }
38
- }
39
- // Try global (Node.js)
40
- if (typeof global !== 'undefined' && global.crypto) {
41
- const crypto = global.crypto;
42
- if (crypto && crypto.subtle) {
43
- return crypto;
44
- }
45
- }
46
- // Fallback - should not happen in proper environments
47
- const debugInfo = {
48
- hasGlobalThis: typeof globalThis !== 'undefined',
49
- hasGlobalThisCrypto: typeof globalThis !== 'undefined' && !!globalThis.crypto,
50
- hasGlobalThisCryptoSubtle: typeof globalThis !== 'undefined' && !!globalThis.crypto && !!globalThis.crypto.subtle,
51
- hasWindow: typeof window !== 'undefined',
52
- hasWindowCrypto: typeof window !== 'undefined' && !!window.crypto,
53
- hasGlobal: typeof global !== 'undefined',
54
- hasGlobalCrypto: typeof global !== 'undefined' && !!global.crypto,
55
- hasGlobalCryptoSubtle: typeof global !== 'undefined' && !!global.crypto && !!global.crypto.subtle
56
- };
57
- throw new Error('crypto.subtle not available. Debug: ' + JSON.stringify(debugInfo));
58
- };
59
- /**
60
- * Generates a new ECC key pair for encryption/decryption.
61
- * Uses P-256 (prime256v1 / NIST P-256) curve which is standard for Web Crypto API.
62
- *
63
- * @returns An object containing the private and public keys as hex strings
64
- */
65
- export function generateKeyPair() {
66
- // Generate a new random private key (32 bytes)
67
- const privateKeyBytes = p256.utils.randomPrivateKey();
68
- // Get public key (uncompressed format: 65 bytes = 0x04 + 32-byte x + 32-byte y)
69
- const publicKeyBytes = p256.getPublicKey(privateKeyBytes, false);
70
- // Convert to hex strings
71
- const privateKey = bytesToHex(privateKeyBytes).padStart(64, '0');
72
- const publicKey = bytesToHex(publicKeyBytes);
73
- return {
74
- privateKey,
75
- publicKey
76
- };
77
- }
78
- /**
79
- * Derives a key from shared secret using SHA-256
80
- */
81
- async function deriveKey(sharedSecret) {
82
- const cryptoAPI = getCrypto();
83
- return await cryptoAPI.subtle.digest('SHA-256', sharedSecret);
84
- }
85
- /**
86
- * Safely converts Uint8Array to base64 string.
87
- * Uses Buffer in Node.js for efficiency, or chunked conversion in browsers
88
- * to avoid call stack overflow with large arrays.
89
- */
90
- function uint8ArrayToBase64(bytes) {
91
- // Check if we're in Node.js and Buffer is available
92
- if (typeof Buffer !== 'undefined' && Buffer.from) {
93
- return Buffer.from(bytes).toString('base64');
94
- }
95
- // Browser fallback: use chunked conversion to avoid call stack overflow
96
- // Chunk size of 8192 (8k) is safe for String.fromCharCode.apply
97
- const CHUNK_SIZE = 8192;
98
- let binaryString = '';
99
- for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
100
- const chunk = bytes.slice(i, i + CHUNK_SIZE);
101
- // Convert chunk to array and use apply for this small chunk
102
- binaryString += String.fromCharCode.apply(null, Array.from(chunk));
103
- }
104
- return btoa(binaryString);
105
- }
106
- /**
107
- * Encrypts a value using ECC public key encryption (ECIES).
108
- * The encrypted output can only be decrypted by the client using their private key.
109
- * This ensures that Avo cannot decrypt the values on the backend.
110
- *
111
- * ECIES uses hybrid encryption (ECDH + AES-256-GCM) which provides:
112
- * - No message size limitations
113
- * - Fast encryption even for large values
114
- * - Strong authentication via GCM
115
- *
116
- * SPECIFICATION (Standard Web Crypto Profile):
117
- * 1. Curve: P-256 (prime256v1 / NIST P-256)
118
- * 2. Key Derivation (KDF): SHA-256(SharedSecret)
119
- * 3. Cipher: AES-256-GCM
120
- * 4. Serialization: [Version(1b)] + [EphemeralPubKey(33 or 65b)] + [IV(16b)] + [AuthTag(16b)] + [Ciphertext]
121
- * Version 0x00 = Standard Web Profile
122
- * EphemeralPubKey: 0x04 (uncompressed) + 64 bytes = 65 bytes total, or compressed format (33 bytes)
123
- *
124
- * @param value - The value to encrypt (any type - will be JSON stringified)
125
- * @param publicKey - The ECC public key in hex format provided by the client
126
- * @returns Promise resolving to base64-encoded encrypted string that can only be decrypted with the private key
127
- */
128
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
- export async function encryptValue(value, publicKey) {
130
- try {
131
- // Convert the value to a JSON string to support all types
132
- // Note: JSON.stringify(undefined) returns undefined (not a string), so handle it explicitly
133
- const stringValue = value === undefined ? 'null' : JSON.stringify(value);
134
- if (stringValue === undefined) {
135
- throw new Error('Cannot encrypt undefined value');
136
- }
137
- // 1. Prepare Public Key
138
- // Ensure publicKey is a string
139
- const publicKeyStr = typeof publicKey === 'string' ? publicKey : String(publicKey);
140
- // Convert recipient's public key from hex to bytes
141
- const recipientPublicKeyBytes = hexToBytes(publicKeyStr);
142
- // 2. Generate Ephemeral Key Pair on P-256
143
- const ephemeralPrivateKeyBytes = p256.utils.randomPrivateKey();
144
- const ephemeralPublicKeyBytes = p256.getPublicKey(ephemeralPrivateKeyBytes, false); // Uncompressed format (65 bytes)
145
- // 3. Derive Shared Secret (ECDH)
146
- // getSharedSecret returns compressed point (33 bytes: 0x02/0x03 + X-coordinate)
147
- // Extract X-coordinate (last 32 bytes) to match elliptic's derive().toArray("be", 32)
148
- const sharedSecretPoint = p256.getSharedSecret(ephemeralPrivateKeyBytes, recipientPublicKeyBytes);
149
- const sharedSecret = sharedSecretPoint.slice(-32); // Extract X-coordinate (last 32 bytes)
150
- // 4. Key Derivation (Hash with SHA-256)
151
- const derivedKeyBuffer = await deriveKey(sharedSecret);
152
- // 5. Import Key for Web Crypto AES-GCM
153
- const cryptoAPI = getCrypto();
154
- const keyMaterial = await cryptoAPI.subtle.importKey('raw', derivedKeyBuffer, { name: 'AES-GCM' }, false, ['encrypt']);
155
- // 6. Generate random IV (16 bytes)
156
- const iv = cryptoAPI.getRandomValues(new Uint8Array(16));
157
- // 7. Encrypt using AES-256-GCM
158
- // Convert string to Uint8Array
159
- const plaintext = new TextEncoder().encode(stringValue);
160
- const encryptedData = await cryptoAPI.subtle.encrypt({
161
- name: 'AES-GCM',
162
- iv,
163
- tagLength: 128 // 16 bytes = 128 bits
164
- }, keyMaterial, plaintext);
165
- // Web Crypto API appends the auth tag to the ciphertext
166
- // We need to extract it separately for our format
167
- const encryptedArray = new Uint8Array(encryptedData);
168
- const authTagLength = 16; // 128 bits = 16 bytes
169
- const ciphertextLength = encryptedArray.length - authTagLength;
170
- const ciphertext = encryptedArray.slice(0, ciphertextLength);
171
- const authTag = encryptedArray.slice(ciphertextLength);
172
- // 8. Serialize Output
173
- // Format: [Version(1)] + [Ephemeral Public Key(65)] + [IV(16)] + [AuthTag(16)] + [Ciphertext]
174
- const version = new Uint8Array([0x00]); // Version 0: Standard Web Profile
175
- // Combine all parts
176
- const resultLength = 1 + ephemeralPublicKeyBytes.length + 16 + 16 + ciphertext.length;
177
- const result = new Uint8Array(resultLength);
178
- let offset = 0;
179
- result.set(version, offset);
180
- offset += 1;
181
- result.set(ephemeralPublicKeyBytes, offset);
182
- offset += ephemeralPublicKeyBytes.length;
183
- result.set(iv, offset);
184
- offset += 16;
185
- result.set(authTag, offset);
186
- offset += 16;
187
- result.set(ciphertext, offset);
188
- // Convert to base64 using safe conversion that handles large arrays
189
- return uint8ArrayToBase64(result);
190
- }
191
- catch (error) {
192
- throw new Error(`Failed to encrypt value. Please check that the public key is valid. Error: ${error instanceof Error ? error.message : String(error)}`);
193
- }
194
- }
195
- /**
196
- * Decrypts a value that was encrypted with encryptValue.
197
- *
198
- * SPECIFICATION (Standard Web Crypto Profile):
199
- * 1. Curve: P-256 (prime256v1 / NIST P-256)
200
- * 2. Key Derivation (KDF): SHA-256(SharedSecret)
201
- * 3. Cipher: AES-256-GCM
202
- * 4. Deserialization: [Version(1b)] + [EphemeralPubKey(33 or 65b)] + [IV(16b)] + [AuthTag(16b)] + [Ciphertext]
203
- *
204
- * @param encryptedValue - The base64-encoded encrypted string
205
- * @param privateKey - The ECC private key in hex format
206
- * @returns Promise resolving to the original decrypted value (parsed from JSON)
207
- */
208
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
- export async function decryptValue(encryptedValue, privateKey) {
210
- try {
211
- // Convert encrypted value from base64 to Uint8Array
212
- const binaryString = atob(encryptedValue);
213
- const encryptedBuffer = new Uint8Array(binaryString.length);
214
- for (let i = 0; i < binaryString.length; i++) {
215
- encryptedBuffer[i] = binaryString.charCodeAt(i);
216
- }
217
- // Minimum size: version(1) + pubkey(33 min) + iv(16) + authTag(16) + ciphertext(1 min) = 67 bytes
218
- if (encryptedBuffer.length < 67) {
219
- throw new Error('Invalid encrypted data: payload too short');
220
- }
221
- // Ensure privateKey is a string
222
- const privateKeyStr = typeof privateKey === 'string' ? privateKey : String(privateKey);
223
- // 1. Deserialize Input
224
- // Format: [Version(1)] + [Ephemeral Public Key(33 or 65)] + [IV(16)] + [AuthTag(16)] + [Ciphertext]
225
- let offset = 0;
226
- const version = encryptedBuffer[offset];
227
- offset += 1;
228
- if (version !== 0x00) {
229
- throw new Error(`Unsupported encryption version: ${version}`);
230
- }
231
- // Check header to determine public key size (0x02/0x03 = 33 bytes compressed, 0x04 = 65 bytes uncompressed)
232
- const keyHeader = encryptedBuffer[offset];
233
- const pubKeySize = keyHeader === 0x02 || keyHeader === 0x03 ? 33 : 65;
234
- // Validate buffer has enough bytes for this key format
235
- const minRequired = 1 + pubKeySize + 16 + 16 + 1;
236
- if (encryptedBuffer.length < minRequired) {
237
- throw new Error(`Invalid encrypted data: expected at least ${minRequired} bytes, got ${encryptedBuffer.length}`);
238
- }
239
- const ephemeralPublicKey = encryptedBuffer.slice(offset, offset + pubKeySize);
240
- offset += pubKeySize;
241
- const iv = encryptedBuffer.slice(offset, offset + 16); // IV (16 bytes)
242
- offset += 16;
243
- const authTag = encryptedBuffer.slice(offset, offset + 16); // Auth tag (16 bytes)
244
- offset += 16;
245
- const ciphertext = encryptedBuffer.slice(offset); // Remaining bytes are ciphertext
246
- // 2. Prepare Private Key
247
- const recipientPrivateKeyBytes = hexToBytes(privateKeyStr);
248
- // 3. Derive Shared Secret (ECDH)
249
- // getSharedSecret returns compressed point (33 bytes: 0x02/0x03 + X-coordinate)
250
- // Extract X-coordinate (last 32 bytes) to match elliptic's derive().toArray("be", 32)
251
- const sharedSecretPoint = p256.getSharedSecret(recipientPrivateKeyBytes, ephemeralPublicKey);
252
- const sharedSecret = sharedSecretPoint.slice(-32); // Extract X-coordinate (last 32 bytes)
253
- // 4. Key Derivation (Hash with SHA-256)
254
- const derivedKeyBuffer = await deriveKey(sharedSecret);
255
- // 5. Import Key for Web Crypto AES-GCM
256
- const cryptoAPI = getCrypto();
257
- const keyMaterial = await cryptoAPI.subtle.importKey('raw', derivedKeyBuffer, { name: 'AES-GCM' }, false, ['decrypt']);
258
- // 6. Decrypt using AES-256-GCM
259
- // Web Crypto API expects the auth tag appended to ciphertext
260
- const combinedLength = ciphertext.length + authTag.length;
261
- const data = new Uint8Array(combinedLength);
262
- data.set(ciphertext, 0);
263
- data.set(authTag, ciphertext.length);
264
- const decryptedBuffer = await cryptoAPI.subtle.decrypt({
265
- name: 'AES-GCM',
266
- iv,
267
- tagLength: 128 // 16 bytes = 128 bits
268
- }, keyMaterial, data);
269
- // 7. Convert to string and parse JSON
270
- const decoder = new TextDecoder();
271
- const stringValue = decoder.decode(decryptedBuffer);
272
- return JSON.parse(stringValue);
273
- }
274
- catch (error) {
275
- 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)}`);
276
- }
277
- }
@@ -1,9 +0,0 @@
1
- export default class AvoGuid {
2
- static newGuid() {
3
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
4
- const r = (Math.random() * 16) | 0;
5
- const v = c === "x" ? r : (r & 0x3) | 0x8;
6
- return v.toString(16);
7
- });
8
- }
9
- }