avo-inspector 2.2.2-alpha.2 → 2.2.2-alpha.4

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 (99) hide show
  1. package/.claude/settings.local.json +18 -0
  2. package/bin/avo-inspector.js +78 -26
  3. package/dist/AvoInspector.d.ts +5 -0
  4. package/dist/AvoNetworkCallsHandler.d.ts +5 -3
  5. package/dist/AvoStreamId.d.ts +58 -0
  6. package/dist/eventSpec/AvoEventSpecCache.d.ts +2 -1
  7. package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +24 -18
  8. package/dist/eventSpec/AvoEventSpecFetcher.d.ts +9 -3
  9. package/dist/eventSpec/fetcher.d.ts +52 -0
  10. package/dist/index.js +1 -1
  11. package/package.json +1 -1
  12. package/rescript/AvoInspector.res +1 -0
  13. package/rescript/AvoInspector.resi +1 -1
  14. package/dist/browser.js.LICENSE.txt +0 -5
  15. package/dist-browser/AvoAnonymousId.d.ts +0 -26
  16. package/dist-browser/AvoBatcher.d.ts +0 -26
  17. package/dist-browser/AvoDeduplicator.d.ts +0 -20
  18. package/dist-browser/AvoEncryption.d.ts +0 -33
  19. package/dist-browser/AvoGuid.d.ts +0 -3
  20. package/dist-browser/AvoInspector.d.ts +0 -78
  21. package/dist-browser/AvoInspectorEnv.d.ts +0 -7
  22. package/dist-browser/AvoNetworkCallsHandler.d.ts +0 -74
  23. package/dist-browser/AvoSchemaParser.d.ts +0 -20
  24. package/dist-browser/AvoStorage.d.ts +0 -22
  25. package/dist-browser/__mocks__/xhr.d.ts +0 -12
  26. package/dist-browser/__mocks__/xhrError.d.ts +0 -12
  27. package/dist-browser/__tests__/AnonymousId_test.d.ts +0 -1
  28. package/dist-browser/__tests__/AvoEncryption_test.d.ts +0 -1
  29. package/dist-browser/__tests__/AvoInspectorEventSpec_test.d.ts +0 -1
  30. package/dist-browser/__tests__/AvoInspector_test.d.ts +0 -1
  31. package/dist-browser/__tests__/BatcherPublic_test.d.ts +0 -1
  32. package/dist-browser/__tests__/Batcher_test.d.ts +0 -1
  33. package/dist-browser/__tests__/Deduplicator_test.d.ts +0 -1
  34. package/dist-browser/__tests__/EncryptedPropertyTracking_test.d.ts +0 -1
  35. package/dist-browser/__tests__/EventSpecCache_test.d.ts +0 -1
  36. package/dist-browser/__tests__/EventSpecFetcher_test.d.ts +0 -1
  37. package/dist-browser/__tests__/EventValidator_test.d.ts +0 -1
  38. package/dist-browser/__tests__/NetworkCallsHandler_test.d.ts +0 -1
  39. package/dist-browser/__tests__/Parsing_test.d.ts +0 -1
  40. package/dist-browser/__tests__/Storage_test.d.ts +0 -1
  41. package/dist-browser/__tests__/ValidationIntegration_test.d.ts +0 -1
  42. package/dist-browser/__tests__/constants.d.ts +0 -36
  43. package/dist-browser/__tests__/helpers/encryptionHelpers.d.ts +0 -13
  44. package/dist-browser/__tests__/helpers/mockFactories.d.ts +0 -49
  45. package/dist-browser/eventSpec/AvoEventSpecCache.d.ts +0 -73
  46. package/dist-browser/eventSpec/AvoEventSpecFetchTypes.d.ts +0 -109
  47. package/dist-browser/eventSpec/AvoEventSpecFetcher.d.ts +0 -55
  48. package/dist-browser/eventSpec/EventValidator.d.ts +0 -90
  49. package/dist-browser/eventSpec/index.d.ts +0 -4
  50. package/dist-browser/index.d.ts +0 -2
  51. package/dist-browser/utils.d.ts +0 -3
  52. package/dist-native/AvoBatcher.d.ts +0 -24
  53. package/dist-native/AvoBatcher.js +0 -82
  54. package/dist-native/AvoDeduplicator.d.ts +0 -36
  55. package/dist-native/AvoDeduplicator.js +0 -142
  56. package/dist-native/AvoGuid.d.ts +0 -3
  57. package/dist-native/AvoGuid.js +0 -14
  58. package/dist-native/AvoInspector.d.ts +0 -52
  59. package/dist-native/AvoInspector.js +0 -220
  60. package/dist-native/AvoInspectorEnv.d.ts +0 -7
  61. package/dist-native/AvoInspectorEnv.js +0 -8
  62. package/dist-native/AvoInstallationId.d.ts +0 -5
  63. package/dist-native/AvoInstallationId.js +0 -36
  64. package/dist-native/AvoNetworkCallsHandler.d.ts +0 -48
  65. package/dist-native/AvoNetworkCallsHandler.js +0 -142
  66. package/dist-native/AvoSchemaParser.d.ts +0 -11
  67. package/dist-native/AvoSchemaParser.js +0 -95
  68. package/dist-native/AvoSessionTracker.d.ts +0 -15
  69. package/dist-native/AvoSessionTracker.js +0 -104
  70. package/dist-native/AvoStorage.d.ts +0 -22
  71. package/dist-native/AvoStorage.js +0 -336
  72. package/dist-native/__mocks__/xhr.d.ts +0 -12
  73. package/dist-native/__mocks__/xhr.js +0 -14
  74. package/dist-native/__mocks__/xhrError.d.ts +0 -12
  75. package/dist-native/__mocks__/xhrError.js +0 -14
  76. package/dist-native/__tests__/AvoInspector.d.ts +0 -1
  77. package/dist-native/__tests__/AvoInspector.js +0 -207
  78. package/dist-native/__tests__/Batcher.d.ts +0 -1
  79. package/dist-native/__tests__/Batcher.js +0 -280
  80. package/dist-native/__tests__/BatcherPublic.d.ts +0 -1
  81. package/dist-native/__tests__/BatcherPublic.js +0 -82
  82. package/dist-native/__tests__/Deduplicator.d.ts +0 -1
  83. package/dist-native/__tests__/Deduplicator.js +0 -111
  84. package/dist-native/__tests__/InstallationId.d.ts +0 -1
  85. package/dist-native/__tests__/InstallationId.js +0 -40
  86. package/dist-native/__tests__/NetworkCallsHandler.d.ts +0 -1
  87. package/dist-native/__tests__/NetworkCallsHandler.js +0 -132
  88. package/dist-native/__tests__/Parsing.d.ts +0 -1
  89. package/dist-native/__tests__/Parsing.js +0 -115
  90. package/dist-native/__tests__/Sessions.d.ts +0 -1
  91. package/dist-native/__tests__/Sessions.js +0 -179
  92. package/dist-native/__tests__/Storage.d.ts +0 -1
  93. package/dist-native/__tests__/Storage.js +0 -173
  94. package/dist-native/__tests__/constants.d.ts +0 -36
  95. package/dist-native/__tests__/constants.js +0 -47
  96. package/dist-native/index.d.ts +0 -2
  97. package/dist-native/index.js +0 -7
  98. package/dist-native/utils.d.ts +0 -3
  99. package/dist-native/utils.js +0 -42
@@ -0,0 +1,18 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm test)",
5
+ "Bash(npm test:*)",
6
+ "Bash(npm run build:*)",
7
+ "Bash(yarn typecheck:*)",
8
+ "Bash(yarn test:*)",
9
+ "Bash(yarn build)",
10
+ "WebSearch",
11
+ "Bash(node -e:*)",
12
+ "Bash(git add:*)",
13
+ "mcp__linear__list_issues"
14
+ ],
15
+ "deny": [],
16
+ "ask": []
17
+ }
18
+ }
@@ -2,6 +2,8 @@
2
2
 
3
3
  // Use eciesjs directly for ECC key generation
4
4
  const { PrivateKey } = require('eciesjs');
5
+ const fs = require('fs');
6
+ const path = require('path');
5
7
 
6
8
  function generateKeyPair() {
7
9
  // Generate a new random private key (32 bytes / 256 bits)
@@ -17,6 +19,7 @@ function generateKeyPair() {
17
19
  }
18
20
 
19
21
  const command = process.argv[2];
22
+ const outputFlag = process.argv[3];
20
23
 
21
24
  function showHelp() {
22
25
  console.log(`
@@ -25,18 +28,24 @@ function showHelp() {
25
28
  ╚═══════════════════════════════════════════════════════════════╝
26
29
 
27
30
  Usage:
28
- npx avo-inspector <command>
31
+ npx avo-inspector <command> [options]
29
32
 
30
33
  Commands:
31
- generate-keys Generate ECC key pair for property encryption
32
- help Show this help message
34
+ generate-keys Generate ECC key pair for property encryption
35
+ help Show this help message
36
+
37
+ Options:
38
+ --output, -o Write keys to files instead of stdout
39
+ Creates: avo-public.key and avo-private.key
33
40
 
34
41
  Examples:
35
- npx avo-inspector generate-keys
42
+ npx avo-inspector generate-keys # Print keys to console
43
+ npx avo-inspector generate-keys --output # Write keys to files
44
+ npx avo-inspector generate-keys -o # Write keys to files (short form)
36
45
  `);
37
46
  }
38
47
 
39
- function generateKeys() {
48
+ function generateKeys(writeToFiles = false) {
40
49
  console.log(`
41
50
  ╔═══════════════════════════════════════════════════════════════╗
42
51
  ║ Generating ECC Key Pair (secp256k1)... ║
@@ -46,26 +55,68 @@ function generateKeys() {
46
55
  try {
47
56
  const { publicKey, privateKey } = generateKeyPair();
48
57
 
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');
58
+ if (writeToFiles) {
59
+ // Write keys to files instead of printing to console
60
+ const publicKeyPath = path.join(process.cwd(), 'avo-public.key');
61
+ const privateKeyPath = path.join(process.cwd(), 'avo-private.key');
62
+
63
+ // Check if files already exist
64
+ if (fs.existsSync(publicKeyPath) || fs.existsSync(privateKeyPath)) {
65
+ console.error('❌ Error: Key files already exist in current directory.');
66
+ console.error(' Delete avo-public.key and avo-private.key first, or use a different directory.\n');
67
+ process.exit(1);
68
+ }
69
+
70
+ // Write public key
71
+ fs.writeFileSync(publicKeyPath, publicKey + '\n', { mode: 0o644 });
72
+ console.log(`✅ Public key written to: ${publicKeyPath}`);
73
+
74
+ // Write private key with restrictive permissions (owner read/write only)
75
+ fs.writeFileSync(privateKeyPath, privateKey + '\n', { mode: 0o600 });
76
+ console.log(`✅ Private key written to: ${privateKeyPath}`);
77
+
78
+ console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
79
+ console.log('⚠️ IMPORTANT: Add avo-private.key to your .gitignore!\n');
80
+ console.log(' echo "avo-private.key" >> .gitignore\n');
81
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
82
+ console.log('📝 Next Steps:\n');
83
+ console.log('1. Use the PUBLIC KEY in your SDK initialization:\n');
84
+ console.log(' const inspector = new AvoInspector({');
85
+ console.log(' apiKey: "your-api-key",');
86
+ console.log(' env: AvoInspectorEnv.Dev,');
87
+ console.log(' version: "1.0.0",');
88
+ console.log(' publicKey: fs.readFileSync("avo-public.key", "utf8").trim()');
89
+ console.log(' });\n');
90
+ console.log('2. Store avo-private.key securely (password manager, vault, etc.)');
91
+ console.log(' - The SDK never uses the private key');
92
+ console.log(' - You only need it to decrypt values in Avo\'s dashboard\n');
93
+ } else {
94
+ // Print keys to console (original behavior)
95
+ console.log('✅ Key pair generated successfully!\n');
96
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
97
+ console.log('📋 PUBLIC KEY (safe to share with Avo SDK):\n');
98
+ console.log(publicKey);
99
+ console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
100
+ console.log('🔐 PRIVATE KEY (⚠️ KEEP SECURE - DO NOT COMMIT TO GIT):\n');
101
+ console.log(privateKey);
102
+ console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
103
+ console.log('💡 TIP: Use --output flag to write keys to files instead:\n');
104
+ console.log(' npx avo-inspector generate-keys --output\n');
105
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
106
+ console.log('📝 Next Steps:\n');
107
+ console.log('1. Use the PUBLIC KEY in your SDK initialization:\n');
108
+ console.log(' const inspector = new AvoInspector({');
109
+ console.log(' apiKey: "your-api-key",');
110
+ console.log(' env: AvoInspectorEnv.Dev,');
111
+ console.log(' version: "1.0.0",');
112
+ console.log(' publicKey: "<paste public key above>"');
113
+ console.log(' });\n');
114
+ console.log(' 💡 The public key is not a secret - you can hardcode it or use .env\n');
115
+ console.log('2. Save the PRIVATE KEY externally (password manager, secure notes)');
116
+ console.log(' - The SDK never uses the private key');
117
+ console.log(' - You only need it to decrypt values in Avo\'s dashboard\n');
118
+ }
119
+
69
120
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
70
121
  console.log('ℹ️ Note: Property encryption only works in dev/staging environments.');
71
122
  console.log(' Production data is never encrypted for performance.\n');
@@ -78,7 +129,8 @@ function generateKeys() {
78
129
  // Main command router
79
130
  switch (command) {
80
131
  case 'generate-keys':
81
- generateKeys();
132
+ const writeToFiles = outputFlag === '--output' || outputFlag === '-o';
133
+ generateKeys(writeToFiles);
82
134
  break;
83
135
  case 'help':
84
136
  case '--help':
@@ -69,8 +69,13 @@ export declare class AvoInspector {
69
69
  /**
70
70
  * Merges validation results into the event schema.
71
71
  * Adds failedEventIds or passedEventIds to each property based on validation.
72
+ * Recursively merges validation results for nested children.
72
73
  */
73
74
  private mergeValidationResults;
75
+ /**
76
+ * Merges validation result into a single property, recursively handling children.
77
+ */
78
+ private mergePropertyValidation;
74
79
  /**
75
80
  * Sends an event immediately with validation data (bypasses batching).
76
81
  * Logs validation info if shouldLog is true.
@@ -29,7 +29,7 @@ export interface BaseBody {
29
29
  trackingId: string;
30
30
  createdAt: string;
31
31
  sessionId: string;
32
- anonymousId: string;
32
+ streamId: string;
33
33
  samplingRate: number;
34
34
  /** Event spec metadata from EventSpecResponse (moved from EventSchemaBody) */
35
35
  eventSpecMetadata?: EventSpecMetadata;
@@ -46,6 +46,8 @@ export interface EventSchemaBody extends BaseBody {
46
46
  eventProperties: EventProperty[];
47
47
  avoFunction: boolean;
48
48
  eventHash: string | null;
49
+ /** Branch ID from getEventSpec response when value validation was performed */
50
+ validatedBranchId?: string;
49
51
  }
50
52
  export declare class AvoNetworkCallsHandler {
51
53
  private readonly apiKey;
@@ -58,9 +60,9 @@ export declare class AvoNetworkCallsHandler {
58
60
  private static readonly trackingEndpoint;
59
61
  constructor(apiKey: string, envName: string, appName: string, appVersion: string, libVersion: string);
60
62
  callInspectorWithBatchBody(inEvents: Array<SessionStartedBody | EventSchemaBody>, onCompleted: (error: Error | null) => any): void;
61
- private fixAnonymousIds;
63
+ private fixStreamIds;
62
64
  bodyForSessionStartedCall(): SessionStartedBody;
63
- bodyForEventSchemaCall(eventName: string, eventProperties: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata): EventSchemaBody;
65
+ bodyForEventSchemaCall(eventName: string, eventProperties: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata, validatedBranchId?: string): EventSchemaBody;
64
66
  private createBaseCallBody;
65
67
  /**
66
68
  * Calls Inspector API immediately with a single event (bypasses batching).
@@ -0,0 +1,58 @@
1
+ /**
2
+ * AvoStreamId manages an ephemeral stream identifier.
3
+ *
4
+ * The stream ID is generated and stored persistently, but resets after:
5
+ * - 4 hours of total age AND
6
+ * - 2 hours of idle time (no activity)
7
+ *
8
+ * This ensures the ID is truly anonymous by not persisting indefinitely,
9
+ * while still maintaining session continuity during active usage.
10
+ */
11
+ export declare class AvoStreamId {
12
+ private static _streamId;
13
+ private static _createdAt;
14
+ private static _lastActivityAt;
15
+ private static readonly MAX_AGE_MS;
16
+ private static readonly IDLE_THRESHOLD_MS;
17
+ private static readonly STREAM_ID_KEY;
18
+ private static readonly CREATED_AT_KEY;
19
+ private static readonly LAST_ACTIVITY_KEY;
20
+ /**
21
+ * Get the stream ID. If it doesn't exist or has expired, generates a new one.
22
+ * Returns "unknown" if storage is not initialized.
23
+ */
24
+ static get streamId(): string;
25
+ /**
26
+ * Check if the stream ID should be reset based on age and idle time.
27
+ */
28
+ private static shouldReset;
29
+ /**
30
+ * Load stream ID and timestamps from storage into cache.
31
+ */
32
+ private static loadFromStorage;
33
+ /**
34
+ * Generate a new stream ID and store it with timestamps.
35
+ */
36
+ private static generateAndStoreNew;
37
+ /**
38
+ * Update the last activity timestamp.
39
+ */
40
+ private static updateLastActivity;
41
+ /**
42
+ * The storage key used to persist the stream ID.
43
+ */
44
+ static get storageKey(): string;
45
+ /**
46
+ * The storage key for created at timestamp.
47
+ */
48
+ static get createdAtKey(): string;
49
+ /**
50
+ * The storage key for last activity timestamp.
51
+ */
52
+ static get lastActivityKey(): string;
53
+ /**
54
+ * Clear the cached stream ID. The next access will reload from storage.
55
+ * This is primarily useful for testing.
56
+ */
57
+ static clearCache(): void;
58
+ }
@@ -46,7 +46,7 @@ export declare class EventSpecCache {
46
46
  */
47
47
  private shouldEvict;
48
48
  /**
49
- * Evicts the oldest cache entry based on timestamp.
49
+ * Evicts the least recently used cache entry based on lastAccessed timestamp.
50
50
  * This implements the LRU (Least Recently Used) eviction policy.
51
51
  */
52
52
  private evictOldest;
@@ -67,6 +67,7 @@ export declare class EventSpecCache {
67
67
  entries: Array<{
68
68
  key: string;
69
69
  age: number;
70
+ lastAccessedAgo: number;
70
71
  eventCount: number;
71
72
  }>;
72
73
  };
@@ -13,13 +13,15 @@ export interface PropertyConstraintsWire {
13
13
  /** Required flag (for reference only, not validated) */
14
14
  r: boolean;
15
15
  /** Pinned values: pinnedValue -> eventIds that require this exact value */
16
- p?: Record<string, string[]>;
16
+ p?: Record<string, Array<string>>;
17
17
  /** Allowed values: JSON array string -> eventIds that accept these values */
18
- v?: Record<string, string[]>;
18
+ v?: Record<string, Array<string>>;
19
19
  /** Regex patterns: pattern -> eventIds that require matching this regex */
20
- rx?: Record<string, string[]>;
20
+ rx?: Record<string, Array<string>>;
21
21
  /** Min/max ranges: "min,max" -> eventIds that require value in this range */
22
- minmax?: Record<string, string[]>;
22
+ minmax?: Record<string, Array<string>>;
23
+ /** Nested property constraints for object properties */
24
+ children?: Record<string, PropertyConstraintsWire>;
23
25
  }
24
26
  /**
25
27
  * Wire format - Event spec entry with short field names.
@@ -32,7 +34,7 @@ export interface EventSpecEntryWire {
32
34
  /** Base event ID */
33
35
  id: string;
34
36
  /** Variant IDs (baseEventId + variantIds = complete set) */
35
- vids: string[];
37
+ vids: Array<string>;
36
38
  /** Property constraints keyed by property name */
37
39
  p: Record<string, PropertyConstraintsWire>;
38
40
  }
@@ -42,7 +44,7 @@ export interface EventSpecEntryWire {
42
44
  */
43
45
  export interface EventSpecResponseWire {
44
46
  /** Array of events matching the requested name */
45
- events: EventSpecEntryWire[];
47
+ events: Array<EventSpecEntryWire>;
46
48
  /** Schema metadata (keeps long names - small, one per response) */
47
49
  metadata: EventSpecMetadata;
48
50
  }
@@ -56,13 +58,15 @@ export interface PropertyConstraints {
56
58
  /** Required flag (for reference only, not validated) */
57
59
  required: boolean;
58
60
  /** Pinned values: pinnedValue -> eventIds that require this exact value */
59
- pinnedValues?: Record<string, string[]>;
61
+ pinnedValues?: Record<string, Array<string>>;
60
62
  /** Allowed values: JSON array string -> eventIds that accept these values */
61
- allowedValues?: Record<string, string[]>;
63
+ allowedValues?: Record<string, Array<string>>;
62
64
  /** Regex patterns: pattern -> eventIds that require matching this regex */
63
- regexPatterns?: Record<string, string[]>;
65
+ regexPatterns?: Record<string, Array<string>>;
64
66
  /** Min/max ranges: "min,max" -> eventIds that require value in this range */
65
- minMaxRanges?: Record<string, string[]>;
67
+ minMaxRanges?: Record<string, Array<string>>;
68
+ /** Nested property constraints for object properties */
69
+ children?: Record<string, PropertyConstraints>;
66
70
  }
67
71
  /**
68
72
  * Internal - Event spec entry with meaningful field names.
@@ -74,7 +78,7 @@ export interface EventSpecEntry {
74
78
  /** Base event ID */
75
79
  baseEventId: string;
76
80
  /** Variant IDs (baseEventId + variantIds = complete set) */
77
- variantIds: string[];
81
+ variantIds: Array<string>;
78
82
  /** Property constraints keyed by property name */
79
83
  props: Record<string, PropertyConstraints>;
80
84
  }
@@ -84,13 +88,11 @@ export interface EventSpecEntry {
84
88
  */
85
89
  export interface EventSpecResponse {
86
90
  /** Array of events matching the requested name */
87
- events: EventSpecEntry[];
91
+ events: Array<EventSpecEntry>;
88
92
  /** Schema metadata */
89
93
  metadata: EventSpecMetadata;
90
94
  }
91
- /**
92
- * Metadata returned with the event spec response.
93
- */
95
+ /** Metadata returned with the event spec response. */
94
96
  export interface EventSpecMetadata {
95
97
  /** Schema identifier */
96
98
  schemaId: string;
@@ -105,8 +107,10 @@ export interface EventSpecMetadata {
105
107
  export interface EventSpecCacheEntry {
106
108
  /** The cached event specification response (internal format) */
107
109
  spec: EventSpecResponse;
108
- /** Timestamp when this entry was cached */
110
+ /** Timestamp when this entry was cached (used for TTL expiration) */
109
111
  timestamp: number;
112
+ /** Timestamp when this entry was last accessed (used for LRU eviction) */
113
+ lastAccessed: number;
110
114
  /** Number of cache hits since this entry was cached */
111
115
  eventCount: number;
112
116
  }
@@ -125,9 +129,11 @@ export interface FetchEventSpecParams {
125
129
  */
126
130
  export interface PropertyValidationResult {
127
131
  /** Event/variant IDs that FAILED validation (present if smaller or equal to passed) */
128
- failedEventIds?: string[];
132
+ failedEventIds?: Array<string>;
129
133
  /** Event/variant IDs that PASSED validation (present if smaller than failed) */
130
- passedEventIds?: string[];
134
+ passedEventIds?: Array<string>;
135
+ /** Nested validation results for child properties of object properties */
136
+ children?: Record<string, PropertyValidationResult>;
131
137
  }
132
138
  /**
133
139
  * Result of validating all properties in an event.
@@ -7,8 +7,8 @@ import type { EventSpecResponse, FetchEventSpecParams } from "./AvoEventSpecFetc
7
7
  /**
8
8
  * EventSpecFetcher handles fetching event specifications from the Avo API.
9
9
  *
10
- * Endpoint: GET /getEventSpec
11
- * Base URL: https://us-central1-avo-web-app.cloudfunctions.net
10
+ * Endpoint: GET /trackingPlan/eventSpec
11
+ * Base URL: https://api.avo.app
12
12
  */
13
13
  export declare class AvoEventSpecFetcher {
14
14
  /** Base URL for the event spec API */
@@ -34,7 +34,7 @@ export declare class AvoEventSpecFetcher {
34
34
  * - The request times out
35
35
  *
36
36
  * This method gracefully degrades - failures do not throw errors.
37
- * When null is returned, validation should be skipped for that event.
37
+ * When null is returned, Phase 2 should skip validation for that event.
38
38
  */
39
39
  fetch(params: FetchEventSpecParams): Promise<EventSpecResponse | null>;
40
40
  /** Internal fetch implementation. */
@@ -51,4 +51,10 @@ export declare class AvoEventSpecFetcher {
51
51
  * Uses short field names from wire format.
52
52
  */
53
53
  private hasExpectedShape;
54
+ /** Parses the wire format response into internal format with meaningful field names. */
55
+ private static parseEventSpecResponse;
56
+ /** Parses a single event spec entry from wire format. */
57
+ private static parseEventSpecEntry;
58
+ /** Parses property constraints from wire format. */
59
+ private static parsePropertyConstraints;
54
60
  }
@@ -0,0 +1,52 @@
1
+ import type { EventSpec, FetchEventSpecParams } from "./types";
2
+ /**
3
+ * EventSpecFetcher handles fetching event specifications from the Avo API.
4
+ *
5
+ * Endpoint: GET /getEventSpec
6
+ * Base URL: https://api.avo.app/inspector/v1
7
+ */
8
+ export declare class EventSpecFetcher {
9
+ /** Base URL for the event spec API */
10
+ private readonly baseUrl;
11
+ /** Network timeout in milliseconds */
12
+ private readonly timeout;
13
+ /** Whether to log debug information */
14
+ private readonly shouldLog;
15
+ /** In-flight requests to prevent duplicate fetches */
16
+ private inFlightRequests;
17
+ constructor(timeout?: number, shouldLog?: boolean, baseUrl?: string);
18
+ /**
19
+ * Generates a unique key for tracking in-flight requests.
20
+ */
21
+ private generateRequestKey;
22
+ /**
23
+ * Fetches an event specification from the API.
24
+ *
25
+ * Returns null if:
26
+ * - The network request fails
27
+ * - The response has an invalid status code (non-200)
28
+ * - The response is invalid or malformed
29
+ * - The request times out
30
+ *
31
+ * This method gracefully degrades - failures do not throw errors.
32
+ * When null is returned, Phase 2 should skip validation for that event.
33
+ */
34
+ fetch(params: FetchEventSpecParams): Promise<EventSpec | null>;
35
+ /**
36
+ * Internal fetch implementation.
37
+ */
38
+ private fetchInternal;
39
+ /**
40
+ * Builds the complete URL with query parameters.
41
+ */
42
+ private buildUrl;
43
+ /**
44
+ * Makes an HTTP GET request using XMLHttpRequest.
45
+ * Returns the parsed JSON response or null on failure.
46
+ */
47
+ private makeRequest;
48
+ /**
49
+ * Validates that the response matches the EventSpec structure.
50
+ */
51
+ private isValidEventSpec;
52
+ }