avo-inspector 2.2.2-alpha → 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.
package/README.md CHANGED
@@ -123,7 +123,7 @@ This will output:
123
123
 
124
124
  ## Using Encryption
125
125
 
126
- Pass the `publicKey` parameter when initializing Inspector:
126
+ Pass the `publicEncryptionKey` parameter when initializing Inspector:
127
127
 
128
128
  ```javascript
129
129
  import * as Inspector from "avo-inspector";
@@ -148,7 +148,7 @@ When encryption is enabled:
148
148
 
149
149
  # Client-Side Validation (Dev/Staging Only)
150
150
 
151
- When initialized with a `publicKey` in dev or staging environments, Inspector performs client-side validation of your events against your Avo Tracking Plan.
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
152
 
153
153
  ## How It Works
154
154
 
@@ -186,13 +186,48 @@ inspector.trackSchemaFromEvent("User Signed Up", { name: "John" });
186
186
  The `trackSchema` method only sends pre-extracted schemas and **does not perform client-side validation** - it goes through the normal batching flow.
187
187
 
188
188
  ```javascript
189
- // ✅ Validates against tracking plan (when publicKey is provided)
189
+ // ✅ Validates against tracking plan (when publicEncryptionKey is provided)
190
190
  inspector.trackSchemaFromEvent("Event Name", { prop: "value" });
191
191
 
192
192
  // ❌ Does NOT validate - only sends schema, uses batching
193
193
  inspector.trackSchema("Event Name", [{ propertyName: "prop", propertyType: "string" }]);
194
194
  ```
195
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
+
196
231
  # Batching control
197
232
 
198
233
  In order to ensure our SDK doesn't have a large impact on performance or battery life it supports event schemas batching.
@@ -1,13 +1,8 @@
1
- import { type AvoNetworkCallsHandler } from "./AvoNetworkCallsHandler";
1
+ import { type AvoNetworkCallsHandler, type EventProperty } from "./AvoNetworkCallsHandler";
2
2
  import type { EventSpecMetadata } from "./eventSpec/AvoEventSpecFetchTypes";
3
- import type { ValidationIssue } from "./eventSpec/EventValidator";
4
3
  export interface AvoBatcherType {
5
4
  handleSessionStarted: () => void;
6
- handleTrackSchema: (eventName: string, schema: Array<{
7
- propertyName: string;
8
- propertyType: string;
9
- children?: any;
10
- }>, eventId: string | null, eventHash: string | null, variantId?: string | null, eventSpecMetadata?: EventSpecMetadata, validationErrors?: ValidationIssue[]) => void;
5
+ handleTrackSchema: (eventName: string, schema: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata) => void;
11
6
  }
12
7
  export declare class AvoBatcher implements AvoBatcherType {
13
8
  private events;
@@ -15,11 +10,7 @@ export declare class AvoBatcher implements AvoBatcherType {
15
10
  private readonly networkCallsHandler;
16
11
  constructor(networkCallsHandler: AvoNetworkCallsHandler);
17
12
  handleSessionStarted(): void;
18
- handleTrackSchema(eventName: string, schema: Array<{
19
- propertyName: string;
20
- propertyType: string;
21
- children?: any;
22
- }>, eventId: string | null, eventHash: string | null, variantId?: string | null, eventSpecMetadata?: EventSpecMetadata, validationErrors?: ValidationIssue[]): void;
13
+ handleTrackSchema(eventName: string, schema: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata): void;
23
14
  private checkIfBatchNeedsToBeSent;
24
15
  private saveEvents;
25
16
  static get cacheKey(): string;
@@ -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 {
@@ -33,12 +34,7 @@ export declare class AvoInspector {
33
34
  suffix?: string;
34
35
  publicEncryptionKey?: string;
35
36
  });
36
- trackSchemaFromEvent(eventName: string, eventProperties: Record<string, any>): Promise<Array<{
37
- propertyName: string;
38
- propertyType: string;
39
- encryptedPropertyValue?: string;
40
- children?: any;
41
- }>>;
37
+ trackSchemaFromEvent(eventName: string, eventProperties: Record<string, any>): Promise<EventProperty[]>;
42
38
  private _avoFunctionTrackSchemaFromEvent;
43
39
  trackSchema(eventName: string, eventSchema: Array<{
44
40
  propertyName: string;
@@ -70,9 +66,14 @@ export declare class AvoInspector {
70
66
  * Note: EventSpec fetching and validation only happens in dev/staging environments.
71
67
  */
72
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;
73
74
  /**
74
75
  * Sends an event immediately with validation data (bypasses batching).
75
- * Logs validation errors if shouldLog is true.
76
+ * Logs validation info if shouldLog is true.
76
77
  */
77
78
  private sendEventWithValidation;
78
79
  }
@@ -1,5 +1,23 @@
1
1
  import type { EventSpecMetadata } from "./eventSpec/AvoEventSpecFetchTypes";
2
- import type { ValidationIssue } from "./eventSpec/EventValidator";
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
+ }
3
21
  export interface BaseBody {
4
22
  apiKey: string;
5
23
  appName: string;
@@ -13,28 +31,19 @@ export interface BaseBody {
13
31
  sessionId: string;
14
32
  anonymousId: string;
15
33
  samplingRate: number;
34
+ /** Event spec metadata from EventSpecResponse (moved from EventSchemaBody) */
35
+ eventSpecMetadata?: EventSpecMetadata;
16
36
  }
17
37
  export interface SessionStartedBody extends BaseBody {
18
38
  type: "sessionStarted";
19
39
  }
20
40
  export interface EventSchemaBody extends BaseBody {
21
41
  type: "event";
22
- /** ID of the matched base event */
42
+ /** ID of the base event from spec (null if no spec available) */
23
43
  eventId: string | null;
24
- /** ID of the matched variant (if any) */
25
- variantId?: string | null;
26
44
  /** Name seen in code */
27
45
  eventName?: string;
28
- /** Event spec metadata from EventSpecResponse */
29
- eventSpecMetadata?: EventSpecMetadata;
30
- /** Array of validation issues found during validation */
31
- validationErrors?: ValidationIssue[];
32
- eventProperties: Array<{
33
- propertyName: string;
34
- propertyType: string;
35
- encryptedPropertyValue?: string;
36
- children?: any;
37
- }>;
46
+ eventProperties: EventProperty[];
38
47
  avoFunction: boolean;
39
48
  eventHash: string | null;
40
49
  }
@@ -51,11 +60,7 @@ export declare class AvoNetworkCallsHandler {
51
60
  callInspectorWithBatchBody(inEvents: Array<SessionStartedBody | EventSchemaBody>, onCompleted: (error: Error | null) => any): void;
52
61
  private fixAnonymousIds;
53
62
  bodyForSessionStartedCall(): SessionStartedBody;
54
- bodyForEventSchemaCall(eventName: string, eventProperties: Array<{
55
- propertyName: string;
56
- propertyType: string;
57
- children?: any;
58
- }>, eventId: string | null, eventHash: string | null, variantId?: string | null, eventSpecMetadata?: EventSpecMetadata, validationErrors?: ValidationIssue[]): EventSchemaBody;
63
+ bodyForEventSchemaCall(eventName: string, eventProperties: EventProperty[], eventId: string | null, eventHash: string | null, eventSpecMetadata?: EventSpecMetadata): EventSchemaBody;
59
64
  private createBaseCallBody;
60
65
  /**
61
66
  * Calls Inspector API immediately with a single event (bypasses batching).
@@ -1,3 +1,4 @@
1
+ import type { EventProperty } from "./AvoNetworkCallsHandler";
1
2
  export declare class AvoSchemaParser {
2
3
  /**
3
4
  * Returns true only if we have a valid encryption key and can send encrypted values.
@@ -9,12 +10,7 @@ export declare class AvoSchemaParser {
9
10
  * Never returns unencrypted values - only encrypted or nothing.
10
11
  */
11
12
  private static getEncryptedPropertyValueIfEnabled;
12
- static extractSchema(eventProperties: Record<string, any>, publicEncryptionKey?: string, env?: string): Array<{
13
- propertyName: string;
14
- propertyType: string;
15
- encryptedPropertyValue?: string;
16
- children?: any;
17
- }>;
13
+ static extractSchema(eventProperties: Record<string, any>, publicEncryptionKey?: string, env?: string): EventProperty[];
18
14
  private static removeDuplicates;
19
15
  private static getPropValueType;
20
16
  }
@@ -4,67 +4,89 @@
4
4
  * changes directly to this file, and we'll apply them to the generator internally.
5
5
  */
6
6
  /**
7
- * Represents the type specification for a property.
8
- * Can be a primitive type or a nested object structure.
7
+ * Wire format - Property constraints with short field names.
8
+ * At most one constraint type will be present per property.
9
9
  */
10
- export interface PropertyTypeSpec {
11
- /** The type category: "primitive" for simple types, "object" for nested structures */
12
- type: "primitive" | "object";
13
- /** For primitive: the type name (e.g., "string", "int"). For object: nested property specs */
14
- value: string | Record<string, PropertySpec>;
10
+ export interface PropertyConstraintsWire {
11
+ /** Type name (for reference only, not validated) */
12
+ t: string;
13
+ /** Required flag (for reference only, not validated) */
14
+ r: boolean;
15
+ /** Pinned values: pinnedValue -> eventIds that require this exact value */
16
+ p?: Record<string, string[]>;
17
+ /** Allowed values: JSON array string -> eventIds that accept these values */
18
+ v?: Record<string, string[]>;
19
+ /** Regex patterns: pattern -> eventIds that require matching this regex */
20
+ rx?: Record<string, string[]>;
21
+ /** Min/max ranges: "min,max" -> eventIds that require value in this range */
22
+ minmax?: Record<string, string[]>;
15
23
  }
16
24
  /**
17
- * Represents the specification for a single property in an event.
18
- * Supports recursive nesting for object types.
25
+ * Wire format - Event spec entry with short field names.
26
+ * A single event entry (base event + its variants).
27
+ * Multiple events can match the same name request due to name mapping.
19
28
  */
20
- export interface PropertySpec {
21
- /** Unique identifier for this property */
29
+ export interface EventSpecEntryWire {
30
+ /** Branch identifier */
31
+ b: string;
32
+ /** Base event ID */
22
33
  id: string;
23
- /** Type specification - contains type category and value */
24
- t: PropertyTypeSpec;
25
- /** Required flag */
26
- r: boolean;
27
- /** Is list (array) flag */
28
- l?: boolean;
29
- /** Minimum value (for numeric types) */
30
- min?: number;
31
- /** Maximum value (for numeric types) */
32
- max?: number;
33
- /** Enum values (allowed values for the property) */
34
- v?: string[];
35
- /** Regex pattern for string validation */
36
- rx?: string;
34
+ /** Variant IDs (baseEventId + variantIds = complete set) */
35
+ vids: string[];
36
+ /** Property constraints keyed by property name */
37
+ p: Record<string, PropertyConstraintsWire>;
37
38
  }
38
39
  /**
39
- * Represents a variant of an event with additional or modified properties.
40
+ * Wire format - Response from getEventSpec endpoint.
41
+ * Contains array of events that match the requested name (due to name mapping).
40
42
  */
41
- export interface VariantSpec {
42
- /** Unique identifier for this variant */
43
- variantId: string;
44
- /** Event ID for this variant (base event id) */
45
- eventId: string;
46
- /** Suffix to append to the base event name */
47
- nameSuffix: string;
48
- /** Name of the variant in this source (if different from canonical name) */
49
- mappedName?: string;
50
- /** Properties specific to this variant */
51
- props: Record<string, PropertySpec>;
43
+ export interface EventSpecResponseWire {
44
+ /** Array of events matching the requested name */
45
+ events: EventSpecEntryWire[];
46
+ /** Schema metadata (keeps long names - small, one per response) */
47
+ metadata: EventSpecMetadata;
52
48
  }
53
49
  /**
54
- * Represents the complete specification for an event, including base event
55
- * and optional variants.
50
+ * Internal - Property constraints with meaningful field names.
51
+ * At most one constraint type will be present per property.
56
52
  */
57
- export interface EventSpec {
58
- /** Unique identifier for this event */
59
- id: string;
60
- /** Canonical Avo name for this event */
61
- name: string;
62
- /** Name of the event in this source (if different from canonical name) */
63
- mappedName?: string;
64
- /** Properties for this event */
65
- props: Record<string, PropertySpec>;
66
- /** Variants of this event with additional/modified properties */
67
- variants: VariantSpec[];
53
+ export interface PropertyConstraints {
54
+ /** Type name (for reference only, not validated) */
55
+ type: string;
56
+ /** Required flag (for reference only, not validated) */
57
+ required: boolean;
58
+ /** Pinned values: pinnedValue -> eventIds that require this exact value */
59
+ pinnedValues?: Record<string, string[]>;
60
+ /** Allowed values: JSON array string -> eventIds that accept these values */
61
+ allowedValues?: Record<string, string[]>;
62
+ /** Regex patterns: pattern -> eventIds that require matching this regex */
63
+ regexPatterns?: Record<string, string[]>;
64
+ /** Min/max ranges: "min,max" -> eventIds that require value in this range */
65
+ minMaxRanges?: Record<string, string[]>;
66
+ }
67
+ /**
68
+ * Internal - Event spec entry with meaningful field names.
69
+ * A single event entry (base event + its variants).
70
+ */
71
+ export interface EventSpecEntry {
72
+ /** Branch identifier */
73
+ branchId: string;
74
+ /** Base event ID */
75
+ baseEventId: string;
76
+ /** Variant IDs (baseEventId + variantIds = complete set) */
77
+ variantIds: string[];
78
+ /** Property constraints keyed by property name */
79
+ props: Record<string, PropertyConstraints>;
80
+ }
81
+ /**
82
+ * Internal - Parsed response from getEventSpec endpoint.
83
+ * Contains array of events that match the requested name (due to name mapping).
84
+ */
85
+ export interface EventSpecResponse {
86
+ /** Array of events matching the requested name */
87
+ events: EventSpecEntry[];
88
+ /** Schema metadata */
89
+ metadata: EventSpecMetadata;
68
90
  }
69
91
  /**
70
92
  * Metadata returned with the event spec response.
@@ -74,28 +96,18 @@ export interface EventSpecMetadata {
74
96
  schemaId: string;
75
97
  /** Branch identifier */
76
98
  branchId: string;
77
- /** Latest action identifier (replaces versionId/createdAt) */
99
+ /** Latest action identifier */
78
100
  latestActionId: string;
79
101
  /** Optional source identifier */
80
102
  sourceId?: string;
81
103
  }
82
- /**
83
- * Response structure from the getEventSpec endpoint.
84
- * Contains an array of potential event matches (due to name mapping).
85
- */
86
- export interface EventSpecResponse {
87
- /** Array of potential event matches */
88
- events: EventSpec[];
89
- /** Metadata about the schema and source */
90
- metadata: EventSpecMetadata;
91
- }
92
104
  /** Cache entry for storing event specs with metadata. */
93
105
  export interface EventSpecCacheEntry {
94
- /** The cached event specification response */
106
+ /** The cached event specification response (internal format) */
95
107
  spec: EventSpecResponse;
96
108
  /** Timestamp when this entry was cached */
97
109
  timestamp: number;
98
- /** Number of events processed since this entry was cached */
110
+ /** Number of cache hits since this entry was cached */
99
111
  eventCount: number;
100
112
  }
101
113
  /** Parameters for fetching event specifications from the API. */
@@ -107,3 +119,23 @@ export interface FetchEventSpecParams {
107
119
  /** The name of the event */
108
120
  eventName: string;
109
121
  }
122
+ /**
123
+ * Result of validating a single property.
124
+ * Contains either failedEventIds or passedEventIds (whichever is smaller for bandwidth).
125
+ */
126
+ export interface PropertyValidationResult {
127
+ /** Event/variant IDs that FAILED validation (present if smaller or equal to passed) */
128
+ failedEventIds?: string[];
129
+ /** Event/variant IDs that PASSED validation (present if smaller than failed) */
130
+ passedEventIds?: string[];
131
+ }
132
+ /**
133
+ * Result of validating all properties in an event.
134
+ * Maps property name to its validation result.
135
+ */
136
+ export interface ValidationResult {
137
+ /** Event spec metadata */
138
+ metadata: EventSpecMetadata | null;
139
+ /** Validation results per property */
140
+ propertyResults: Record<string, PropertyValidationResult>;
141
+ }
@@ -47,8 +47,8 @@ export declare class AvoEventSpecFetcher {
47
47
  */
48
48
  private makeRequest;
49
49
  /**
50
- * Basic shape check - ensures response has the minimum expected structure.
51
- * Does not perform deep validation of all fields.
50
+ * Basic shape check for wire format - ensures response has the minimum expected structure.
51
+ * Uses short field names from wire format.
52
52
  */
53
53
  private hasExpectedShape;
54
54
  }
@@ -1,45 +1,16 @@
1
1
  /**
2
2
  * EventValidator - Client-side validation of tracking events against the Avo Tracking Plan.
3
3
  *
4
- * This module handles:
5
- * 1. Matching: Identifying the most likely Event and Variant from specs
6
- * 2. Validation: Checking runtime properties against the matched spec rules
7
- */
8
- import type { EventSpecResponse, EventSpec, EventSpecMetadata, VariantSpec } from "./AvoEventSpecFetchTypes";
9
- /**
10
- * Validation issue codes for client-side validation.
11
- */
12
- export type ValidationIssueCode = "RequiredMissing" | "ValueBelowMin" | "ValueAboveMax" | "RegexMismatch" | "NotInAllowedValues" | "UnexpectedProperty" | "UnexpectedEvent" | "TypeMismatch";
13
- /**
14
- * Represents a validation issue found during event validation.
4
+ * This module validates property values against constraints:
5
+ * - Pinned values (exact match required)
6
+ * - Allowed values (must be in list)
7
+ * - Regex patterns (must match pattern)
8
+ * - Min/max ranges (numeric values must be in range)
15
9
  *
16
- * Note: We do NOT include the received value to avoid sending user data to the backend.
17
- * The backend can correlate issues with encrypted values via propertyName matching
18
- * against eventProperties.encryptedPropertyValue in the payload.
10
+ * No schema validation (types/required) is performed - only value constraints.
11
+ * Validation runs against ALL events/variants in the response.
19
12
  */
20
- export interface ValidationIssue {
21
- /** The type of validation issue */
22
- code: ValidationIssueCode;
23
- /** Property ID (preferred over name) */
24
- propertyId?: string;
25
- /** Property name - used to correlate with encryptedPropertyValue in eventProperties */
26
- propertyName?: string;
27
- /** Expected value for the validation rule (from spec, not user data) */
28
- expected?: string | number | boolean;
29
- }
30
- /**
31
- * Result of the validation process.
32
- */
33
- export interface ValidationResult {
34
- /** ID of the matched event, null if no match found */
35
- eventId: string | null;
36
- /** ID of the matched variant, null if base event or no match */
37
- variantId: string | null;
38
- /** Event spec metadata from the response */
39
- eventSpecMetadata: EventSpecMetadata;
40
- /** List of all validation issues found */
41
- validationErrors: ValidationIssue[];
42
- }
13
+ import type { EventSpecResponse, ValidationResult } from "./AvoEventSpecFetchTypes";
43
14
  /**
44
15
  * Runtime property value - can be any JSON-compatible type
45
16
  */
@@ -49,42 +20,15 @@ export type RuntimePropertyValue = string | number | boolean | null | undefined
49
20
  */
50
21
  export type RuntimeProperties = Record<string, RuntimePropertyValue>;
51
22
  /**
52
- * Internal match result for scoring
53
- */
54
- interface MatchCandidate {
55
- event: EventSpec;
56
- variant: VariantSpec | null;
57
- score: number;
58
- }
59
- /**
60
- * Validates a runtime event against the EventSpecResponse.
23
+ * Validates runtime properties against all events in the EventSpecResponse.
24
+ *
25
+ * For each property:
26
+ * - If property not in spec: no validation needed (empty result)
27
+ * - If property in spec: check constraints and collect failed/passed eventIds
28
+ * - Return whichever list is smaller for bandwidth optimization
61
29
  *
62
- * @param eventName - The event name observed at runtime
63
30
  * @param properties - The properties observed at runtime
64
31
  * @param specResponse - The EventSpecResponse from the backend
65
- * @returns ValidationResult with match info and any validation errors
66
- */
67
- export declare function validateEvent(eventName: string, properties: RuntimeProperties, specResponse: EventSpecResponse): ValidationResult;
68
- /**
69
- * Finds the closest matching Event and Variant for the runtime data.
70
- *
71
- * @param eventName - Runtime event name
72
- * @param properties - Runtime properties
73
- * @param events - Array of EventSpecs to match against
74
- * @returns The best matching event and variant, or null if no match
75
- */
76
- export declare function findClosestMatch(eventName: string, properties: RuntimeProperties, events: EventSpec[]): MatchCandidate | null;
77
- /**
78
- * Validates runtime properties against the matched event and variant specs.
79
- *
80
- * @param properties - Runtime properties
81
- * @param event - Matched EventSpec
82
- * @param variant - Matched VariantSpec (or null for base event)
83
- * @returns Array of validation issues found
84
- */
85
- export declare function validateProperties(properties: RuntimeProperties, event: EventSpec, variant: VariantSpec | null): ValidationIssue[];
86
- /**
87
- * Quick check if an event exists in the spec (without full validation).
32
+ * @returns ValidationResult with baseEventId, metadata, and per-property results
88
33
  */
89
- export declare function eventExistsInSpec(eventName: string, specResponse: EventSpecResponse): boolean;
90
- export {};
34
+ export declare function validateEvent(properties: RuntimeProperties, specResponse: EventSpecResponse): ValidationResult;