avo-inspector 2.2.0 → 2.2.2-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 (108) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/README.md +130 -0
  3. package/bin/avo-inspector.js +97 -0
  4. package/dist/AvoBatcher.d.ts +4 -11
  5. package/dist/AvoEncryption.d.ts +33 -0
  6. package/dist/AvoInspector.d.ts +35 -6
  7. package/dist/AvoNetworkCallsHandler.d.ts +42 -12
  8. package/dist/AvoSchemaParser.d.ts +12 -5
  9. package/dist/browser.js.LICENSE.txt +5 -0
  10. package/dist/eventSpec/AvoEventSpecCache.d.ts +73 -0
  11. package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +141 -0
  12. package/dist/eventSpec/AvoEventSpecFetcher.d.ts +54 -0
  13. package/dist/eventSpec/EventValidator.d.ts +34 -0
  14. package/dist/eventSpec/cache.d.ts +73 -0
  15. package/dist/eventSpec/index.d.ts +4 -0
  16. package/dist/eventSpec/types.d.ts +74 -0
  17. package/dist/index.js +2 -1
  18. package/dist/index.js.LICENSE.txt +5 -0
  19. package/dist-browser/AvoAnonymousId.d.ts +26 -0
  20. package/dist-browser/AvoBatcher.d.ts +26 -0
  21. package/dist-browser/AvoDeduplicator.d.ts +20 -0
  22. package/dist-browser/AvoEncryption.d.ts +33 -0
  23. package/dist-browser/AvoGuid.d.ts +3 -0
  24. package/dist-browser/AvoInspector.d.ts +78 -0
  25. package/dist-browser/AvoInspectorEnv.d.ts +7 -0
  26. package/dist-browser/AvoNetworkCallsHandler.d.ts +74 -0
  27. package/dist-browser/AvoSchemaParser.d.ts +20 -0
  28. package/dist-browser/AvoStorage.d.ts +22 -0
  29. package/dist-browser/__mocks__/xhr.d.ts +12 -0
  30. package/dist-browser/__mocks__/xhrError.d.ts +12 -0
  31. package/dist-browser/__tests__/AnonymousId_test.d.ts +1 -0
  32. package/dist-browser/__tests__/AvoEncryption_test.d.ts +1 -0
  33. package/dist-browser/__tests__/AvoInspectorEventSpec_test.d.ts +1 -0
  34. package/dist-browser/__tests__/AvoInspector_test.d.ts +1 -0
  35. package/dist-browser/__tests__/BatcherPublic_test.d.ts +1 -0
  36. package/dist-browser/__tests__/Batcher_test.d.ts +1 -0
  37. package/dist-browser/__tests__/Deduplicator_test.d.ts +1 -0
  38. package/dist-browser/__tests__/EncryptedPropertyTracking_test.d.ts +1 -0
  39. package/dist-browser/__tests__/EventSpecCache_test.d.ts +1 -0
  40. package/dist-browser/__tests__/EventSpecFetcher_test.d.ts +1 -0
  41. package/dist-browser/__tests__/EventValidator_test.d.ts +1 -0
  42. package/dist-browser/__tests__/NetworkCallsHandler_test.d.ts +1 -0
  43. package/dist-browser/__tests__/Parsing_test.d.ts +1 -0
  44. package/dist-browser/__tests__/Storage_test.d.ts +1 -0
  45. package/dist-browser/__tests__/ValidationIntegration_test.d.ts +1 -0
  46. package/dist-browser/__tests__/constants.d.ts +36 -0
  47. package/dist-browser/__tests__/helpers/encryptionHelpers.d.ts +13 -0
  48. package/dist-browser/__tests__/helpers/mockFactories.d.ts +49 -0
  49. package/dist-browser/eventSpec/AvoEventSpecCache.d.ts +73 -0
  50. package/dist-browser/eventSpec/AvoEventSpecFetchTypes.d.ts +109 -0
  51. package/dist-browser/eventSpec/AvoEventSpecFetcher.d.ts +55 -0
  52. package/dist-browser/eventSpec/EventValidator.d.ts +90 -0
  53. package/dist-browser/eventSpec/index.d.ts +4 -0
  54. package/dist-browser/index.d.ts +2 -0
  55. package/dist-browser/utils.d.ts +3 -0
  56. package/dist-native/AvoBatcher.d.ts +24 -0
  57. package/dist-native/AvoBatcher.js +82 -0
  58. package/dist-native/AvoDeduplicator.d.ts +36 -0
  59. package/dist-native/AvoDeduplicator.js +142 -0
  60. package/dist-native/AvoGuid.d.ts +3 -0
  61. package/dist-native/AvoGuid.js +14 -0
  62. package/dist-native/AvoInspector.d.ts +52 -0
  63. package/dist-native/AvoInspector.js +220 -0
  64. package/dist-native/AvoInspectorEnv.d.ts +7 -0
  65. package/dist-native/AvoInspectorEnv.js +8 -0
  66. package/dist-native/AvoInstallationId.d.ts +5 -0
  67. package/dist-native/AvoInstallationId.js +36 -0
  68. package/dist-native/AvoNetworkCallsHandler.d.ts +48 -0
  69. package/dist-native/AvoNetworkCallsHandler.js +142 -0
  70. package/dist-native/AvoSchemaParser.d.ts +11 -0
  71. package/dist-native/AvoSchemaParser.js +95 -0
  72. package/dist-native/AvoSessionTracker.d.ts +15 -0
  73. package/dist-native/AvoSessionTracker.js +104 -0
  74. package/dist-native/AvoStorage.d.ts +22 -0
  75. package/dist-native/AvoStorage.js +336 -0
  76. package/dist-native/__mocks__/xhr.d.ts +12 -0
  77. package/dist-native/__mocks__/xhr.js +14 -0
  78. package/dist-native/__mocks__/xhrError.d.ts +12 -0
  79. package/dist-native/__mocks__/xhrError.js +14 -0
  80. package/dist-native/__tests__/AvoInspector.d.ts +1 -0
  81. package/dist-native/__tests__/AvoInspector.js +207 -0
  82. package/dist-native/__tests__/Batcher.d.ts +1 -0
  83. package/dist-native/__tests__/Batcher.js +280 -0
  84. package/dist-native/__tests__/BatcherPublic.d.ts +1 -0
  85. package/dist-native/__tests__/BatcherPublic.js +82 -0
  86. package/dist-native/__tests__/Deduplicator.d.ts +1 -0
  87. package/dist-native/__tests__/Deduplicator.js +111 -0
  88. package/dist-native/__tests__/InstallationId.d.ts +1 -0
  89. package/dist-native/__tests__/InstallationId.js +40 -0
  90. package/dist-native/__tests__/NetworkCallsHandler.d.ts +1 -0
  91. package/dist-native/__tests__/NetworkCallsHandler.js +132 -0
  92. package/dist-native/__tests__/Parsing.d.ts +1 -0
  93. package/dist-native/__tests__/Parsing.js +115 -0
  94. package/dist-native/__tests__/Sessions.d.ts +1 -0
  95. package/dist-native/__tests__/Sessions.js +179 -0
  96. package/dist-native/__tests__/Storage.d.ts +1 -0
  97. package/dist-native/__tests__/Storage.js +173 -0
  98. package/dist-native/__tests__/constants.d.ts +36 -0
  99. package/dist-native/__tests__/constants.js +47 -0
  100. package/dist-native/index.d.ts +2 -0
  101. package/dist-native/index.js +7 -0
  102. package/dist-native/utils.d.ts +3 -0
  103. package/dist-native/utils.js +42 -0
  104. package/jest.config.js +6 -0
  105. package/package.json +8 -2
  106. package/webpack.browserConfig.js +1 -1
  107. package/webpack.config.js +1 -1
  108. package/.claude/settings.local.json +0 -9
package/CHANGELOG.md CHANGED
@@ -1,3 +1,36 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.2.1-alpha] - 2025-11-24
9
+
10
+ ### Added
11
+
12
+ - **ECC Property Value Encryption**: Optional `publicEncryptionKey` parameter on SDK initialization enables zero-knowledge encryption of property values in dev/staging environments using ECIES (Elliptic Curve Integrated Encryption Scheme). Avo never has access to the private key, ensuring complete data privacy.
13
+ - Uses secp256k1 curve with AES-256-GCM for hybrid encryption
14
+ - Only encrypts in dev/staging environments (production sends schema only)
15
+ - Adds optional `encryptedPropertyValue` field to event schema
16
+ - CLI tool for key generation: `npx avo-inspector generate-keys`
17
+ - Note: Key generation and decryption are not exported from SDK (use CLI tool for keys, decryption happens in Avo's dashboard)
18
+
19
+ - **EventSpec Fetching**: Automatically fetches and caches event specifications from Avo API in dev/staging environments
20
+ - Non-blocking async API calls
21
+ - In-memory caching for performance
22
+ - NOT called in production (prod remains simple and fast)
23
+ - Foundation for Phase 2: event validation
24
+
25
+ ### Changed
26
+
27
+ - Production environment now optimized: no property values, no encryption, no EventSpec API calls
28
+ - Dev/Staging environments now support rich debugging with encrypted values and event specs
29
+
30
+ ### Dependencies
31
+
32
+ - Added `eciesjs` ^0.4.11 for ECC/ECIES encryption
33
+
1
34
  ## 2.2.0
2
35
 
3
36
  - Add anonymous ID support to track events without user identification
@@ -22,4 +55,4 @@ Security update
22
55
 
23
56
  ## 1.3.0
24
57
 
25
- Multiple Avo Inspector instances support, with the optional `suffix` constructor parameter
58
+ Multiple Avo Inspector instances support, with the optional `suffix` constructor parameter
package/README.md CHANGED
@@ -98,6 +98,136 @@ let schema = inspector.extractSchema({
98
98
 
99
99
  You can experiment with this method to see how more complex schemas look, for example with nested lists and objects.
100
100
 
101
+ # Property Value Encryption (Dev/Staging Only)
102
+
103
+ Inspector supports encrypting property values in development and staging environments. This allows you to see actual property values in Avo's debugging tools without exposing sensitive data, since only you hold the private decryption key.
104
+
105
+ ## Key Features
106
+
107
+ - Uses ECC (Elliptic Curve Cryptography) with secp256k1 for strong security
108
+ - Zero-knowledge architecture: Avo never has access to your private key
109
+ - No message size limitations
110
+ - Only active in dev/staging environments (production is unaffected)
111
+
112
+ ## Generating Keys
113
+
114
+ Use the built-in CLI tool to generate a key pair:
115
+
116
+ ```bash
117
+ npx avo-inspector generate-keys
118
+ ```
119
+
120
+ This will output:
121
+ - **Public key**: Pass this to the SDK to enable encryption
122
+ - **Private key**: Save this externally (you'll use it to decrypt values in Avo's dashboard)
123
+
124
+ ## Using Encryption
125
+
126
+ Pass the `publicEncryptionKey` parameter when initializing Inspector:
127
+
128
+ ```javascript
129
+ import * as Inspector from "avo-inspector";
130
+
131
+ let inspector = new Inspector.AvoInspector({
132
+ apiKey: "your api key",
133
+ env: Inspector.AvoInspectorEnv.Dev,
134
+ version: "1.0.0",
135
+ publicEncryptionKey: "your-public-key-hex-string" // Enable encryption
136
+ });
137
+ ```
138
+
139
+ **Note:** The public key is not a secret - you can hardcode it, store it in `.env`, or configure it however you prefer. The SDK only uses the public key to encrypt values before sending them to Avo.
140
+
141
+ **Private key:** Save it securely (password manager, secure notes). Never expose it to the SDK. You only need it when viewing encrypted values in Avo's dashboard.
142
+
143
+ When encryption is enabled:
144
+ - Property values are encrypted before being sent to Avo
145
+ - You can decrypt them in Avo's dashboard using your private key
146
+ - Works with all data types: strings, numbers, booleans, objects, arrays, null
147
+ - Handles large payloads (1KB+) without issues
148
+
149
+ # Client-Side Validation (Dev/Staging Only)
150
+
151
+ When initialized with a `publicEncryptionKey` in dev or staging environments, Inspector performs client-side validation of your events against your Avo Tracking Plan.
152
+
153
+ ## How It Works
154
+
155
+ 1. **Event Spec Fetching**: When you track an event, Inspector fetches the event specification from Avo's backend (results are cached for performance).
156
+
157
+ 2. **Event Matching**: Inspector matches your event to the closest event in your tracking plan, considering event names, mapped names, and variant-specific properties.
158
+
159
+ 3. **Property Validation**: Your event properties are validated against the spec rules:
160
+ - Required properties are present
161
+ - Property types match (string, int, float, boolean, object, list)
162
+ - Numeric values are within min/max bounds
163
+ - String values match regex patterns
164
+ - Enum values are in the allowed list
165
+ - Pinned values match exactly (variant-specific fixed values)
166
+ - No unexpected properties are sent
167
+
168
+ 4. **Immediate Reporting**: Validated events bypass batching and are sent immediately to Inspector, ensuring you get real-time feedback.
169
+
170
+ ## Validation Errors
171
+
172
+ If logging is enabled, validation errors are logged to the console:
173
+
174
+ ```javascript
175
+ inspector.enableLogging(true);
176
+
177
+ // If "User Signed Up" requires an "email" property:
178
+ inspector.trackSchemaFromEvent("User Signed Up", { name: "John" });
179
+ // Console: [Avo Inspector] Validation errors for event "User Signed Up": [{ code: "RequiredMissing", propertyName: "email" }]
180
+ ```
181
+
182
+ ## Important: trackSchema Does Not Validate
183
+
184
+ **Note:** Client-side validation only works with `trackSchemaFromEvent`, which has access to actual property values needed for validation.
185
+
186
+ The `trackSchema` method only sends pre-extracted schemas and **does not perform client-side validation** - it goes through the normal batching flow.
187
+
188
+ ```javascript
189
+ // ✅ Validates against tracking plan (when publicEncryptionKey is provided)
190
+ inspector.trackSchemaFromEvent("Event Name", { prop: "value" });
191
+
192
+ // ❌ Does NOT validate - only sends schema, uses batching
193
+ inspector.trackSchema("Event Name", [{ propertyName: "prop", propertyType: "string" }]);
194
+ ```
195
+
196
+ ## Accessing Validation Results
197
+
198
+ `trackSchemaFromEvent` returns a `Promise<EventProperty[]>` that resolves with the validated properties. You can use this in two ways:
199
+
200
+ ### Fire-and-Forget (Default)
201
+
202
+ Call without `await` for non-blocking behavior. The event is tracked asynchronously and validation happens in the background:
203
+
204
+ ```javascript
205
+ // Non-blocking - validation runs in background
206
+ inspector.trackSchemaFromEvent("Event Name", { prop: "value" });
207
+ ```
208
+
209
+ ### Await for Validation Results
210
+
211
+ If you need to access validation results (e.g., for testing or debugging), you can `await` the call:
212
+
213
+ ```javascript
214
+ // Blocking - wait for validation to complete
215
+ const validatedProperties = await inspector.trackSchemaFromEvent("Event Name", {
216
+ email: "user@example.com",
217
+ age: 25
218
+ });
219
+
220
+ // Each property includes validation results:
221
+ // {
222
+ // propertyName: "email",
223
+ // propertyType: "string",
224
+ // failedEventIds: [], // Event IDs where this property failed validation
225
+ // passedEventIds: ["abc123"] // Event IDs where this property passed validation
226
+ // }
227
+ ```
228
+
229
+ **Note:** Awaiting will block until the event spec is fetched (if not cached) and validation completes. For production use, fire-and-forget is recommended to avoid impacting your application's performance.
230
+
101
231
  # Batching control
102
232
 
103
233
  In order to ensure our SDK doesn't have a large impact on performance or battery life it supports event schemas batching.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Use eciesjs directly for ECC key generation
4
+ const { PrivateKey } = require('eciesjs');
5
+
6
+ function generateKeyPair() {
7
+ // Generate a new random private key (32 bytes / 256 bits)
8
+ const privateKey = new PrivateKey();
9
+
10
+ // Derive the public key from the private key
11
+ const publicKey = privateKey.publicKey;
12
+
13
+ return {
14
+ publicKey: publicKey.toHex(),
15
+ privateKey: privateKey.toHex()
16
+ };
17
+ }
18
+
19
+ const command = process.argv[2];
20
+
21
+ function showHelp() {
22
+ console.log(`
23
+ ╔═══════════════════════════════════════════════════════════════╗
24
+ ║ AVO INSPECTOR CLI ║
25
+ ╚═══════════════════════════════════════════════════════════════╝
26
+
27
+ Usage:
28
+ npx avo-inspector <command>
29
+
30
+ Commands:
31
+ generate-keys Generate ECC key pair for property encryption
32
+ help Show this help message
33
+
34
+ Examples:
35
+ npx avo-inspector generate-keys
36
+ `);
37
+ }
38
+
39
+ function generateKeys() {
40
+ console.log(`
41
+ ╔═══════════════════════════════════════════════════════════════╗
42
+ ║ Generating ECC Key Pair (secp256k1)... ║
43
+ ╚═══════════════════════════════════════════════════════════════╝
44
+ `);
45
+
46
+ try {
47
+ const { publicKey, privateKey } = generateKeyPair();
48
+
49
+ console.log('✅ Key pair generated successfully!\n');
50
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
51
+ console.log('📋 PUBLIC KEY (safe to share with Avo SDK):\n');
52
+ console.log(publicKey);
53
+ console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
54
+ console.log('🔐 PRIVATE KEY (⚠️ KEEP SECURE - DO NOT COMMIT TO GIT):\n');
55
+ console.log(privateKey);
56
+ console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
57
+ console.log('📝 Next Steps:\n');
58
+ console.log('1. Use the PUBLIC KEY in your SDK initialization:\n');
59
+ console.log(' const inspector = new AvoInspector({');
60
+ console.log(' apiKey: "your-api-key",');
61
+ console.log(' env: AvoInspectorEnv.Dev,');
62
+ console.log(' version: "1.0.0",');
63
+ console.log(' publicKey: "<paste public key above>"');
64
+ console.log(' });\n');
65
+ console.log(' 💡 The public key is not a secret - you can hardcode it or use .env\n');
66
+ console.log('2. Save the PRIVATE KEY externally (password manager, secure notes)');
67
+ console.log(' - The SDK never uses the private key');
68
+ console.log(' - You only need it to decrypt values in Avo\'s dashboard\n');
69
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
70
+ console.log('ℹ️ Note: Property encryption only works in dev/staging environments.');
71
+ console.log(' Production data is never encrypted for performance.\n');
72
+ } catch (error) {
73
+ console.error('❌ Error generating keys:', error.message);
74
+ process.exit(1);
75
+ }
76
+ }
77
+
78
+ // Main command router
79
+ switch (command) {
80
+ case 'generate-keys':
81
+ generateKeys();
82
+ break;
83
+ case 'help':
84
+ case '--help':
85
+ case '-h':
86
+ showHelp();
87
+ break;
88
+ case undefined:
89
+ console.error('❌ No command specified\n');
90
+ showHelp();
91
+ process.exit(1);
92
+ break;
93
+ default:
94
+ console.error(`❌ Unknown command: ${command}\n`);
95
+ showHelp();
96
+ process.exit(1);
97
+ }
@@ -1,11 +1,8 @@
1
- import { type AvoNetworkCallsHandler } from "./AvoNetworkCallsHandler";
1
+ import { type AvoNetworkCallsHandler, type EventProperty } from "./AvoNetworkCallsHandler";
2
+ import type { EventSpecMetadata } from "./eventSpec/AvoEventSpecFetchTypes";
2
3
  export interface AvoBatcherType {
3
4
  handleSessionStarted: () => void;
4
- handleTrackSchema: (eventName: string, schema: Array<{
5
- propertyName: string;
6
- propertyType: string;
7
- children?: any;
8
- }>, eventId: string | null, eventHash: string | null) => void;
5
+ handleTrackSchema: (eventName: string, schema: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata) => void;
9
6
  }
10
7
  export declare class AvoBatcher implements AvoBatcherType {
11
8
  private events;
@@ -13,11 +10,7 @@ export declare class AvoBatcher implements AvoBatcherType {
13
10
  private readonly networkCallsHandler;
14
11
  constructor(networkCallsHandler: AvoNetworkCallsHandler);
15
12
  handleSessionStarted(): void;
16
- handleTrackSchema(eventName: string, schema: Array<{
17
- propertyName: string;
18
- propertyType: string;
19
- children?: any;
20
- }>, eventId: string | null, eventHash: string | null): void;
13
+ handleTrackSchema(eventName: string, schema: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata): void;
21
14
  private checkIfBatchNeedsToBeSent;
22
15
  private saveEvents;
23
16
  static get cacheKey(): string;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Generates a new ECC key pair for encryption/decryption.
3
+ * Uses secp256k1 curve (P-256 family) which provides 128-bit security.
4
+ *
5
+ * @returns An object containing the private and public keys as hex strings
6
+ */
7
+ export declare function generateKeyPair(): {
8
+ privateKey: string;
9
+ publicKey: string;
10
+ };
11
+ /**
12
+ * Encrypts a value using ECC public key encryption (ECIES).
13
+ * The encrypted output can only be decrypted by the client using their private key.
14
+ * This ensures that Avo cannot decrypt the values on the backend.
15
+ *
16
+ * ECIES uses hybrid encryption (ECDH + AES-256-GCM) which provides:
17
+ * - No message size limitations
18
+ * - Fast encryption even for large values
19
+ * - Strong authentication via GCM
20
+ *
21
+ * @param value - The value to encrypt (any type - will be JSON stringified)
22
+ * @param publicKey - The ECC public key in hex format provided by the client
23
+ * @returns Base64-encoded encrypted string that can only be decrypted with the private key
24
+ */
25
+ export declare function encryptValue(value: any, publicKey: string): string;
26
+ /**
27
+ * Decrypts a value that was encrypted with encryptValue.
28
+ *
29
+ * @param encryptedValue - The base64-encoded encrypted string
30
+ * @param privateKey - The ECC private key in hex format
31
+ * @returns The original decrypted value (parsed from JSON)
32
+ */
33
+ export declare function decryptValue(encryptedValue: string, privateKey: string): any;
@@ -1,5 +1,6 @@
1
1
  import { type AvoInspectorEnvValueType } from "./AvoInspectorEnv";
2
2
  import { AvoBatcher } from "./AvoBatcher";
3
+ import { type EventProperty } from "./AvoNetworkCallsHandler";
3
4
  import { AvoStorage } from "./AvoStorage";
4
5
  import { AvoDeduplicator } from "./AvoDeduplicator";
5
6
  export declare class AvoInspector {
@@ -8,6 +9,11 @@ export declare class AvoInspector {
8
9
  avoDeduplicator: AvoDeduplicator;
9
10
  apiKey: string;
10
11
  version: string;
12
+ private avoNetworkCallsHandler;
13
+ private publicEncryptionKey?;
14
+ private streamId?;
15
+ private eventSpecCache?;
16
+ private eventSpecFetcher?;
11
17
  static avoStorage: AvoStorage;
12
18
  private static _batchSize;
13
19
  static get batchSize(): number;
@@ -26,25 +32,48 @@ export declare class AvoInspector {
26
32
  version: string;
27
33
  appName?: string;
28
34
  suffix?: string;
35
+ publicEncryptionKey?: string;
29
36
  });
30
- trackSchemaFromEvent(eventName: string, eventProperties: Record<string, any>): Array<{
31
- propertyName: string;
32
- propertyType: string;
33
- children?: any;
34
- }>;
37
+ trackSchemaFromEvent(eventName: string, eventProperties: Record<string, any>): Promise<EventProperty[]>;
35
38
  private _avoFunctionTrackSchemaFromEvent;
36
39
  trackSchema(eventName: string, eventSchema: Array<{
37
40
  propertyName: string;
38
41
  propertyType: string;
42
+ encryptedPropertyValue?: string;
39
43
  children?: any;
40
- }>): void;
44
+ }>): Promise<void>;
41
45
  private trackSchemaInternal;
42
46
  enableLogging(enable: boolean): void;
43
47
  extractSchema(eventProperties: Record<string, any>, shouldLogIfEnabled?: boolean): Array<{
44
48
  propertyName: string;
45
49
  propertyType: string;
50
+ encryptedPropertyValue?: string;
46
51
  children?: any;
47
52
  }>;
48
53
  setBatchSize(newBatchSize: number): void;
49
54
  setBatchFlushSeconds(newBatchFlushSeconds: number): void;
55
+ /**
56
+ * Fetches the event spec if spec fetching is enabled.
57
+ * Used by trackSchema when we don't have raw properties to validate.
58
+ *
59
+ * Note: EventSpec fetching only happens in dev/staging environments.
60
+ */
61
+ private fetchEventSpecIfNeeded;
62
+ /**
63
+ * Fetches event spec and validates the event against it.
64
+ * Returns ValidationResult if spec is available, null otherwise.
65
+ *
66
+ * Note: EventSpec fetching and validation only happens in dev/staging environments.
67
+ */
68
+ private fetchAndValidateEvent;
69
+ /**
70
+ * Merges validation results into the event schema.
71
+ * Adds failedEventIds or passedEventIds to each property based on validation.
72
+ */
73
+ private mergeValidationResults;
74
+ /**
75
+ * Sends an event immediately with validation data (bypasses batching).
76
+ * Logs validation info if shouldLog is true.
77
+ */
78
+ private sendEventWithValidation;
50
79
  }
@@ -1,3 +1,23 @@
1
+ import type { EventSpecMetadata } from "./eventSpec/AvoEventSpecFetchTypes";
2
+ /**
3
+ * Recursive type for schema children.
4
+ * - For object properties: array of EventProperty objects
5
+ * - For list properties: array of strings (primitive types) or nested structures
6
+ */
7
+ export type SchemaChild = string | EventProperty | SchemaChild[];
8
+ /**
9
+ * Property schema with optional validation results.
10
+ */
11
+ export interface EventProperty {
12
+ propertyName: string;
13
+ propertyType: string;
14
+ encryptedPropertyValue?: string;
15
+ children?: SchemaChild[];
16
+ /** Event/variant IDs that FAILED validation (present if smaller or equal to passed) */
17
+ failedEventIds?: string[];
18
+ /** Event/variant IDs that PASSED validation (present if smaller than failed) */
19
+ passedEventIds?: string[];
20
+ }
1
21
  export interface BaseBody {
2
22
  apiKey: string;
3
23
  appName: string;
@@ -11,20 +31,20 @@ export interface BaseBody {
11
31
  sessionId: string;
12
32
  anonymousId: string;
13
33
  samplingRate: number;
34
+ /** Event spec metadata from EventSpecResponse (moved from EventSchemaBody) */
35
+ eventSpecMetadata?: EventSpecMetadata;
14
36
  }
15
37
  export interface SessionStartedBody extends BaseBody {
16
38
  type: "sessionStarted";
17
39
  }
18
40
  export interface EventSchemaBody extends BaseBody {
19
41
  type: "event";
20
- eventName: string;
21
- eventProperties: Array<{
22
- propertyName: string;
23
- propertyType: string;
24
- children?: any;
25
- }>;
26
- avoFunction: boolean;
42
+ /** ID of the base event from spec (null if no spec available) */
27
43
  eventId: string | null;
44
+ /** Name seen in code */
45
+ eventName?: string;
46
+ eventProperties: EventProperty[];
47
+ avoFunction: boolean;
28
48
  eventHash: string | null;
29
49
  }
30
50
  export declare class AvoNetworkCallsHandler {
@@ -40,10 +60,20 @@ export declare class AvoNetworkCallsHandler {
40
60
  callInspectorWithBatchBody(inEvents: Array<SessionStartedBody | EventSchemaBody>, onCompleted: (error: Error | null) => any): void;
41
61
  private fixAnonymousIds;
42
62
  bodyForSessionStartedCall(): SessionStartedBody;
43
- bodyForEventSchemaCall(eventName: string, eventProperties: Array<{
44
- propertyName: string;
45
- propertyType: string;
46
- children?: any;
47
- }>, eventId: string | null, eventHash: string | null): EventSchemaBody;
63
+ bodyForEventSchemaCall(eventName: string, eventProperties: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata): EventSchemaBody;
48
64
  private createBaseCallBody;
65
+ /**
66
+ * Calls Inspector API immediately with a single event (bypasses batching).
67
+ * Used when event spec validation is available.
68
+ * Note: Does not drop due to sampling - validated events are always sent.
69
+ */
70
+ callInspectorImmediately(eventBody: EventSchemaBody, onCompleted: (error: Error | null) => any): void;
71
+ /**
72
+ * Check if event should be dropped based on sampling rate.
73
+ */
74
+ private shouldDropBySampling;
75
+ /**
76
+ * Core Inspector API call logic shared by batch and immediate calls.
77
+ */
78
+ private callInspectorApi;
49
79
  }
@@ -1,9 +1,16 @@
1
+ import type { EventProperty } from "./AvoNetworkCallsHandler";
1
2
  export declare class AvoSchemaParser {
2
- static extractSchema(eventProperties: Record<string, any>): Array<{
3
- propertyName: string;
4
- propertyType: string;
5
- children?: any;
6
- }>;
3
+ /**
4
+ * Returns true only if we have a valid encryption key and can send encrypted values.
5
+ * If no key is present, returns false and no property values will be sent.
6
+ */
7
+ private static canSendEncryptedValues;
8
+ /**
9
+ * Returns the encrypted property value if encryption is enabled, otherwise undefined.
10
+ * Never returns unencrypted values - only encrypted or nothing.
11
+ */
12
+ private static getEncryptedPropertyValueIfEnabled;
13
+ static extractSchema(eventProperties: Record<string, any>, publicEncryptionKey?: string, env?: string): EventProperty[];
7
14
  private static removeDuplicates;
8
15
  private static getPropValueType;
9
16
  }
@@ -0,0 +1,5 @@
1
+ /*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
2
+
3
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
4
+
5
+ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
@@ -0,0 +1,73 @@
1
+ import type { EventSpecResponse } from "./AvoEventSpecFetchTypes";
2
+ /**
3
+ * EventSpecCache implements a dual-condition cache with LRU eviction.
4
+ *
5
+ * Cache Policy:
6
+ * - Entries expire after 5 minutes OR 50 cache hits, whichever comes first
7
+ * - When 50 cache hits occur globally, the oldest cached entry is evicted
8
+ * - Each cache entry tracks: spec, timestamp, and hit count
9
+ *
10
+ * Cache Key Format: ${apiKey}:${streamId}:${eventName}
11
+ *
12
+ * Note: Event count only increments on cache hits, not on every tracked event.
13
+ * This ensures the cache evicts based on actual usage, not overall tracking volume.
14
+ */
15
+ export declare class EventSpecCache {
16
+ /** Cache storage: key -> CacheEntry */
17
+ private cache;
18
+ /** Time-to-live in milliseconds (5 minutes) */
19
+ private readonly TTL_MS;
20
+ /** Maximum cache hit count before rotating cache (50 hits) */
21
+ private readonly MAX_EVENT_COUNT;
22
+ /** Global cache hit counter to track when to rotate cache */
23
+ private globalEventCount;
24
+ /** Whether to log debug information */
25
+ private readonly shouldLog;
26
+ constructor(shouldLog?: boolean);
27
+ /**
28
+ * Generates a cache key from the provided parameters.
29
+ */
30
+ private generateKey;
31
+ /**
32
+ * Retrieves an event spec response from the cache if it exists and is valid.
33
+ * Returns null if the entry is missing, expired, or has exceeded event count.
34
+ *
35
+ * On cache hit, increments the hit count for this entry and the global counter.
36
+ */
37
+ get(apiKey: string, streamId: string, eventName: string): EventSpecResponse | null;
38
+ /**
39
+ * Stores an event spec response in the cache.
40
+ */
41
+ set(apiKey: string, streamId: string, eventName: string, spec: EventSpecResponse): void;
42
+ /**
43
+ * Determines if a cache entry should be evicted based on:
44
+ * - Age (older than 5 minutes)
45
+ * - Hit count (50 or more cache hits)
46
+ */
47
+ private shouldEvict;
48
+ /**
49
+ * Evicts the oldest cache entry based on timestamp.
50
+ * This implements the LRU (Least Recently Used) eviction policy.
51
+ */
52
+ private evictOldest;
53
+ /**
54
+ * Clears all cached entries. Useful for testing.
55
+ */
56
+ clear(): void;
57
+ /**
58
+ * Returns the current size of the cache.
59
+ */
60
+ size(): number;
61
+ /**
62
+ * Returns cache statistics for debugging.
63
+ */
64
+ getStats(): {
65
+ size: number;
66
+ globalEventCount: number;
67
+ entries: Array<{
68
+ key: string;
69
+ age: number;
70
+ eventCount: number;
71
+ }>;
72
+ };
73
+ }