react-native-avo-inspector
Version:
[](https://badge.fury.io/js/react-native-avo-inspector)
847 lines (846 loc) • 36.8 kB
JavaScript
"use strict";
/**
* EventValidator - Client-side validation of tracking events against the Avo Tracking Plan.
*
* This module validates property values against constraints:
* - Pinned values (exact match required)
* - Allowed values (must be in list)
* - Regex patterns (must match pattern, with safe-regex2 check and 1s timeout)
* - Min/max ranges (numeric values must be in range)
*
* No schema validation (types/required) is performed - only value constraints.
* Validation runs against ALL events/variants in the response.
*
* Adapted for React Native: uses safe-regex2 for regex safety and
* Promise.race + clearTimeout for per-match 1-second timeout.
*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateEvent = validateEvent;
var safe = require("safe-regex2");
// =============================================================================
// HELPER FUNCTIONS FOR NESTED PROPERTIES
// =============================================================================
/**
* Deep copies a constraint mapping (pinnedValues, allowedValues, etc.),
* including the arrays inside to avoid shared references.
*/
function deepCopyConstraintMapping(mapping) {
var result = {};
for (var _i = 0, _a = Object.entries(mapping); _i < _a.length; _i++) {
var _b = _a[_i], key = _b[0], arr = _b[1];
result[key] = __spreadArray([], arr, true);
}
return result;
}
/**
* Deep copies children constraints recursively.
*/
function deepCopyChildren(children) {
var result = {};
for (var _i = 0, _a = Object.entries(children); _i < _a.length; _i++) {
var _b = _a[_i], propName = _b[0], constraints = _b[1];
result[propName] = {
type: constraints.type,
required: constraints.required,
pinnedValues: constraints.pinnedValues
? deepCopyConstraintMapping(constraints.pinnedValues)
: undefined,
allowedValues: constraints.allowedValues
? deepCopyConstraintMapping(constraints.allowedValues)
: undefined,
regexPatterns: constraints.regexPatterns
? deepCopyConstraintMapping(constraints.regexPatterns)
: undefined,
minMaxRanges: constraints.minMaxRanges
? deepCopyConstraintMapping(constraints.minMaxRanges)
: undefined,
children: constraints.children
? deepCopyChildren(constraints.children)
: undefined
};
}
return result;
}
/**
* Merges children constraints from source into target recursively.
*/
function mergeChildren(target, source) {
for (var _i = 0, _a = Object.entries(source); _i < _a.length; _i++) {
var _b = _a[_i], propName = _b[0], sourceConstraints = _b[1];
if (!target[propName]) {
// New child property - deep copy it
target[propName] = {
type: sourceConstraints.type,
required: sourceConstraints.required,
pinnedValues: sourceConstraints.pinnedValues
? deepCopyConstraintMapping(sourceConstraints.pinnedValues)
: undefined,
allowedValues: sourceConstraints.allowedValues
? deepCopyConstraintMapping(sourceConstraints.allowedValues)
: undefined,
regexPatterns: sourceConstraints.regexPatterns
? deepCopyConstraintMapping(sourceConstraints.regexPatterns)
: undefined,
minMaxRanges: sourceConstraints.minMaxRanges
? deepCopyConstraintMapping(sourceConstraints.minMaxRanges)
: undefined,
children: sourceConstraints.children
? deepCopyChildren(sourceConstraints.children)
: undefined
};
}
else {
// Merge into existing child property
var targetConstraints = target[propName];
mergeConstraintMappings(targetConstraints, sourceConstraints);
// Recursively merge nested children
if (sourceConstraints.children) {
if (!targetConstraints.children) {
targetConstraints.children = deepCopyChildren(sourceConstraints.children);
}
else {
mergeChildren(targetConstraints.children, sourceConstraints.children);
}
}
}
}
}
/**
* Merges constraint mappings (pinnedValues, allowedValues, etc.) from source into target.
*/
function mergeConstraintMappings(target, source) {
if (source.pinnedValues) {
if (!target.pinnedValues) {
target.pinnedValues = {};
}
for (var _i = 0, _a = Object.keys(source.pinnedValues); _i < _a.length; _i++) {
var key = _a[_i];
if (target.pinnedValues[key]) {
var merged = new Set(target.pinnedValues[key]);
for (var _b = 0, _c = source.pinnedValues[key]; _b < _c.length; _b++) {
var id = _c[_b];
merged.add(id);
}
target.pinnedValues[key] = Array.from(merged);
}
else {
target.pinnedValues[key] = __spreadArray([], source.pinnedValues[key], true);
}
}
}
if (source.allowedValues) {
if (!target.allowedValues) {
target.allowedValues = {};
}
for (var _d = 0, _e = Object.keys(source.allowedValues); _d < _e.length; _d++) {
var key = _e[_d];
if (target.allowedValues[key]) {
var merged = new Set(target.allowedValues[key]);
for (var _f = 0, _g = source.allowedValues[key]; _f < _g.length; _f++) {
var id = _g[_f];
merged.add(id);
}
target.allowedValues[key] = Array.from(merged);
}
else {
target.allowedValues[key] = __spreadArray([], source.allowedValues[key], true);
}
}
}
if (source.regexPatterns) {
if (!target.regexPatterns) {
target.regexPatterns = {};
}
for (var _h = 0, _j = Object.keys(source.regexPatterns); _h < _j.length; _h++) {
var key = _j[_h];
if (target.regexPatterns[key]) {
var merged = new Set(target.regexPatterns[key]);
for (var _k = 0, _l = source.regexPatterns[key]; _k < _l.length; _k++) {
var id = _l[_k];
merged.add(id);
}
target.regexPatterns[key] = Array.from(merged);
}
else {
target.regexPatterns[key] = __spreadArray([], source.regexPatterns[key], true);
}
}
}
if (source.minMaxRanges) {
if (!target.minMaxRanges) {
target.minMaxRanges = {};
}
for (var _m = 0, _o = Object.keys(source.minMaxRanges); _m < _o.length; _m++) {
var key = _o[_m];
if (target.minMaxRanges[key]) {
var merged = new Set(target.minMaxRanges[key]);
for (var _p = 0, _q = source.minMaxRanges[key]; _p < _q.length; _p++) {
var id = _q[_p];
merged.add(id);
}
target.minMaxRanges[key] = Array.from(merged);
}
else {
target.minMaxRanges[key] = __spreadArray([], source.minMaxRanges[key], true);
}
}
}
}
// =============================================================================
// CACHES
// =============================================================================
/**
* Cache for compiled regex objects to avoid recompilation on every event.
* Patterns are expected to be stable per session.
* Only safe patterns (per safe-regex2) are cached.
*/
var regexCache = new Map();
/**
* Gets a compiled regex from cache or compiles and caches it.
* Uses safe-regex2 to validate patterns before compilation.
* Returns null if the pattern is unsafe or invalid.
*/
function getOrCompileRegex(pattern) {
var regex = regexCache.get(pattern);
if (regex) {
return regex;
}
// Check pattern safety with safe-regex2
if (!safe(pattern)) {
console.warn("[Avo Inspector] Warning: unsafe regex pattern skipped: ".concat(pattern));
return null;
}
try {
regex = new RegExp(pattern);
regexCache.set(pattern, regex);
return regex;
}
catch (e) {
console.warn("[Avo Inspector] Warning: invalid regex pattern skipped: ".concat(pattern));
return null;
}
}
/**
* Tests a regex match with a 1-second timeout using Promise.race + clearTimeout.
* The timer is cleared on regex completion to prevent timer accumulation.
* Returns true if pattern matches, false if not or on timeout.
*
* Note: safe-regex2 is the primary ReDoS protection — it screens out catastrophic
* patterns before they are compiled. This timeout is a safety net for future async
* regex engines or edge cases where safe-regex2 does not catch the pattern.
*/
function testRegexWithTimeout(regex_1, value_1) {
return __awaiter(this, arguments, void 0, function (regex, value, timeoutMs) {
if (timeoutMs === void 0) { timeoutMs = 1000; }
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve) {
var settled = false;
var timer = setTimeout(function () {
if (!settled) {
settled = true;
console.warn("[Avo Inspector] Warning: regex match timed out after ".concat(timeoutMs, "ms"));
resolve(false);
}
}, timeoutMs);
try {
var result = regex.test(value);
if (!settled) {
settled = true;
clearTimeout(timer);
resolve(result);
}
}
catch (e) {
if (!settled) {
settled = true;
clearTimeout(timer);
resolve(false);
}
}
})];
});
});
}
/**
* Cache for parsed allowed values JSON.
* Key: JSON string, Value: Set of allowed values for O(1) lookup.
*/
var allowedValuesCache = new Map();
/**
* Parses allowed values JSON string and returns a Set for O(1) lookup.
* Results are cached to avoid repeated JSON.parse calls.
* @returns Set of allowed values, or null if JSON is invalid
*/
function getOrParseAllowedValues(jsonString) {
var allowedSet = allowedValuesCache.get(jsonString);
if (!allowedSet) {
try {
var allowedArray = JSON.parse(jsonString);
allowedSet = new Set(allowedArray);
allowedValuesCache.set(jsonString, allowedSet);
}
catch (e) {
// Invalid JSON - return null
return null;
}
}
return allowedSet;
}
// =============================================================================
// MAIN VALIDATION FUNCTION
// =============================================================================
/**
* Validates runtime properties against all events in the EventSpecResponse.
*
* For each property:
* - If property not in spec: no validation needed (empty result)
* - If property in spec: check constraints and collect failed/passed eventIds
* - Return whichever list is smaller for bandwidth optimization
*
* This is an async function because regex validation uses Promise.race for timeout.
*
* @param properties - The properties observed at runtime
* @param specResponse - The EventSpecResponse from the backend
* @returns ValidationResult with metadata and per-property results
*/
function validateEvent(properties, specResponse) {
return __awaiter(this, void 0, void 0, function () {
var allEventIds, constraintsByProperty, propertyResults, _i, _a, propName, value, constraints, result;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
allEventIds = collectAllEventIds(specResponse.events);
constraintsByProperty = collectConstraintsByPropertyName(specResponse.events);
propertyResults = {};
_i = 0, _a = Object.keys(properties);
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 5];
propName = _a[_i];
value = properties[propName];
constraints = constraintsByProperty[propName];
if (!!constraints) return [3 /*break*/, 2];
// Property not in spec - no constraints to fail
propertyResults[propName] = {};
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, validatePropertyConstraints(value, constraints, allEventIds)];
case 3:
result = _b.sent();
propertyResults[propName] = result;
_b.label = 4;
case 4:
_i++;
return [3 /*break*/, 1];
case 5: return [2 /*return*/, {
metadata: specResponse.metadata,
propertyResults: propertyResults
}];
}
});
});
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
/**
* Collects all eventIds (baseEventId + variantIds) from all events.
*/
function collectAllEventIds(events) {
var ids = [];
for (var _i = 0, events_1 = events; _i < events_1.length; _i++) {
var event_1 = events_1[_i];
ids.push(event_1.baseEventId);
for (var j = 0; j < event_1.variantIds.length; j++) {
ids.push(event_1.variantIds[j]);
}
}
return ids;
}
/**
* Collects all property constraints from all events into a single lookup table.
*/
function collectConstraintsByPropertyName(events) {
// Fast path: no events
if (events.length === 0) {
return {};
}
// Fast path: single event, return props directly (no aggregation needed)
if (events.length === 1) {
return events[0].props;
}
// Multiple events: aggregate constraints from all events
var result = {};
for (var _i = 0, events_2 = events; _i < events_2.length; _i++) {
var event_2 = events_2[_i];
for (var _a = 0, _b = Object.entries(event_2.props); _a < _b.length; _a++) {
var _c = _b[_a], propName = _c[0], constraints = _c[1];
if (!result[propName]) {
// First time seeing this property - initialize with copies
result[propName] = {
type: constraints.type,
required: constraints.required,
pinnedValues: constraints.pinnedValues
? __assign({}, constraints.pinnedValues) : undefined,
allowedValues: constraints.allowedValues
? __assign({}, constraints.allowedValues) : undefined,
regexPatterns: constraints.regexPatterns
? __assign({}, constraints.regexPatterns) : undefined,
minMaxRanges: constraints.minMaxRanges
? __assign({}, constraints.minMaxRanges) : undefined,
children: constraints.children
? deepCopyChildren(constraints.children)
: undefined
};
}
else {
// Aggregate constraint mappings from additional events
var existing = result[propName];
mergeConstraintMappings(existing, constraints);
// Recursively merge nested children
if (constraints.children) {
if (!existing.children) {
existing.children = deepCopyChildren(constraints.children);
}
else {
mergeChildren(existing.children, constraints.children);
}
}
}
}
}
return result;
}
/**
* Maximum nesting depth for recursive value validation.
*/
var MAX_CHILD_DEPTH = 2;
/**
* Validates a property value against its constraints.
*/
function validatePropertyConstraints(value_1, constraints_1, allEventIds_1) {
return __awaiter(this, arguments, void 0, function (value, constraints, allEventIds, depth) {
var result;
if (depth === void 0) { depth = 0; }
return __generator(this, function (_a) {
result = {};
if (depth >= MAX_CHILD_DEPTH) {
return [2 /*return*/, result];
}
// Handle list types (isList=true)
if (constraints.isList) {
return [2 /*return*/, validateListProperty(value, constraints, allEventIds, depth)];
}
// Handle nested object properties with children (single object, not list)
if (constraints.children) {
return [2 /*return*/, validateObjectProperty(value, constraints, allEventIds, depth)];
}
// For primitive properties only: skip validation for null/undefined on non-required properties.
if ((value === null || value === undefined) && !constraints.required) {
return [2 /*return*/, result];
}
// Validate value constraints for primitive properties
return [2 /*return*/, validatePrimitiveProperty(value, constraints, allEventIds)];
});
});
}
/**
* Validates a list property (array of items).
*/
function validateListProperty(value, constraints, allEventIds, depth) {
return __awaiter(this, void 0, void 0, function () {
var result, childrenResults, _loop_1, _i, _a, _b, childName, childConstraints, failedIds, _c, value_1, item;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
result = {};
if (!Array.isArray(value)) {
return [2 /*return*/, result];
}
if (!constraints.children) return [3 /*break*/, 5];
childrenResults = {};
_loop_1 = function (childName, childConstraints) {
var aggregatedFailedIds, _e, value_2, item, itemObj, childValue, childResult, _f, _g, id, passedSet, _h, allEventIds_1, id, failedArray, passedIds;
return __generator(this, function (_j) {
switch (_j.label) {
case 0:
aggregatedFailedIds = new Set();
_e = 0, value_2 = value;
_j.label = 1;
case 1:
if (!(_e < value_2.length)) return [3 /*break*/, 4];
item = value_2[_e];
itemObj = (typeof item === "object" && item !== null && !Array.isArray(item))
? item
: {};
childValue = itemObj[childName];
return [4 /*yield*/, validatePropertyConstraints(childValue, childConstraints, allEventIds, depth + 1)];
case 2:
childResult = _j.sent();
if (childResult.failedEventIds) {
for (_f = 0, _g = childResult.failedEventIds; _f < _g.length; _f++) {
id = _g[_f];
aggregatedFailedIds.add(id);
}
}
if (childResult.passedEventIds) {
passedSet = new Set(childResult.passedEventIds);
for (_h = 0, allEventIds_1 = allEventIds; _h < allEventIds_1.length; _h++) {
id = allEventIds_1[_h];
if (!passedSet.has(id)) {
aggregatedFailedIds.add(id);
}
}
}
_j.label = 3;
case 3:
_e++;
return [3 /*break*/, 1];
case 4:
if (aggregatedFailedIds.size > 0) {
failedArray = Array.from(aggregatedFailedIds);
passedIds = allEventIds.filter(function (id) { return !aggregatedFailedIds.has(id); });
if (passedIds.length < failedArray.length && passedIds.length > 0) {
childrenResults[childName] = { passedEventIds: passedIds };
}
else {
childrenResults[childName] = { failedEventIds: failedArray };
}
}
return [2 /*return*/];
}
});
};
_i = 0, _a = Object.entries(constraints.children);
_d.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
_b = _a[_i], childName = _b[0], childConstraints = _b[1];
return [5 /*yield**/, _loop_1(childName, childConstraints)];
case 2:
_d.sent();
_d.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4:
if (Object.keys(childrenResults).length > 0) {
result.children = childrenResults;
}
return [2 /*return*/, result];
case 5:
failedIds = new Set();
_c = 0, value_1 = value;
_d.label = 6;
case 6:
if (!(_c < value_1.length)) return [3 /*break*/, 10];
item = value_1[_c];
if (constraints.pinnedValues) {
checkPinnedValues(item, constraints.pinnedValues, failedIds);
}
if (constraints.allowedValues) {
checkAllowedValues(item, constraints.allowedValues, failedIds);
}
if (!constraints.regexPatterns) return [3 /*break*/, 8];
return [4 /*yield*/, checkRegexPatterns(item, constraints.regexPatterns, failedIds)];
case 7:
_d.sent();
_d.label = 8;
case 8:
if (constraints.minMaxRanges) {
checkMinMaxRanges(item, constraints.minMaxRanges, failedIds);
}
_d.label = 9;
case 9:
_c++;
return [3 /*break*/, 6];
case 10: return [2 /*return*/, buildValidationResult(failedIds, allEventIds)];
}
});
});
}
/**
* Validates an object property (single object with children).
*/
function validateObjectProperty(value, constraints, allEventIds, depth) {
return __awaiter(this, void 0, void 0, function () {
var result, childrenResults, valueObj, _i, _a, _b, childName, childConstraints, childValue, childResult;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
result = {};
childrenResults = {};
valueObj = (typeof value === "object" && value !== null && !Array.isArray(value))
? value
: {};
_i = 0, _a = Object.entries(constraints.children);
_c.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
_b = _a[_i], childName = _b[0], childConstraints = _b[1];
childValue = valueObj[childName];
return [4 /*yield*/, validatePropertyConstraints(childValue, childConstraints, allEventIds, depth + 1)];
case 2:
childResult = _c.sent();
if (childResult.failedEventIds || childResult.passedEventIds || childResult.children) {
childrenResults[childName] = childResult;
}
_c.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4:
if (Object.keys(childrenResults).length > 0) {
result.children = childrenResults;
}
return [2 /*return*/, result];
}
});
});
}
/**
* Validates a primitive property (not list, not object with children).
*/
function validatePrimitiveProperty(value, constraints, allEventIds) {
return __awaiter(this, void 0, void 0, function () {
var failedIds;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
failedIds = new Set();
if (constraints.pinnedValues) {
checkPinnedValues(value, constraints.pinnedValues, failedIds);
}
if (constraints.allowedValues) {
checkAllowedValues(value, constraints.allowedValues, failedIds);
}
if (!constraints.regexPatterns) return [3 /*break*/, 2];
return [4 /*yield*/, checkRegexPatterns(value, constraints.regexPatterns, failedIds)];
case 1:
_a.sent();
_a.label = 2;
case 2:
if (constraints.minMaxRanges) {
checkMinMaxRanges(value, constraints.minMaxRanges, failedIds);
}
return [2 /*return*/, buildValidationResult(failedIds, allEventIds)];
}
});
});
}
/**
* Builds the validation result from failed IDs, returning whichever list is smaller.
*/
function buildValidationResult(failedIds, allEventIds) {
var passedIds = allEventIds.filter(function (id) { return !failedIds.has(id); });
var failedArray = Array.from(failedIds);
if (failedArray.length === 0 && passedIds.length === 0) {
return {};
}
// Prefer passedEventIds only when strictly smaller than failedEventIds
if (passedIds.length < failedArray.length && passedIds.length > 0) {
return { passedEventIds: passedIds };
}
else if (failedArray.length > 0) {
return { failedEventIds: failedArray };
}
else {
return {};
}
}
// =============================================================================
// CONSTRAINT VALIDATION FUNCTIONS
// =============================================================================
/**
* Adds all IDs from the array to the set.
*/
function addIdsToSet(ids, set) {
for (var _i = 0, ids_1 = ids; _i < ids_1.length; _i++) {
var id = ids_1[_i];
set.add(id);
}
}
/**
* Converts runtime value to string for comparison.
*/
function convertValueToString(value) {
if (value === null ||
value === undefined ||
typeof value === "boolean" ||
typeof value === "number" ||
typeof value === "string") {
return String(value);
}
if (Array.isArray(value) || typeof value === "object") {
try {
return JSON.stringify(value);
}
catch (e) {
console.warn("[Avo Inspector] Failed to stringify value: ".concat(e));
return String(value);
}
}
return String(value);
}
/**
* Checks pinned values constraint.
*/
function checkPinnedValues(value, pinnedValues, failedIds) {
var stringValue = convertValueToString(value);
for (var _i = 0, _a = Object.entries(pinnedValues); _i < _a.length; _i++) {
var _b = _a[_i], pinnedValue = _b[0], eventIds = _b[1];
if (stringValue !== pinnedValue) {
addIdsToSet(eventIds, failedIds);
}
}
}
/**
* Checks allowed values constraint.
*/
function checkAllowedValues(value, allowedValues, failedIds) {
var stringValue = convertValueToString(value);
for (var _i = 0, _a = Object.entries(allowedValues); _i < _a.length; _i++) {
var _b = _a[_i], allowedArrayJson = _b[0], eventIds = _b[1];
var allowedSet = getOrParseAllowedValues(allowedArrayJson);
if (allowedSet === null) {
console.warn("[Avo Inspector] Invalid allowed values JSON: ".concat(allowedArrayJson));
continue;
}
if (!allowedSet.has(stringValue)) {
addIdsToSet(eventIds, failedIds);
}
}
}
/**
* Checks regex pattern constraint with safe-regex2 validation and 1-second timeout.
* Unsafe patterns are skipped with a console.warn.
*/
function checkRegexPatterns(value, regexPatterns, failedIds) {
return __awaiter(this, void 0, void 0, function () {
var _i, _a, eventIds, _b, _c, _d, pattern, eventIds, regex, matched;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
// Only check regex for string values
if (typeof value !== "string") {
for (_i = 0, _a = Object.values(regexPatterns); _i < _a.length; _i++) {
eventIds = _a[_i];
addIdsToSet(eventIds, failedIds);
}
return [2 /*return*/];
}
_b = 0, _c = Object.entries(regexPatterns);
_e.label = 1;
case 1:
if (!(_b < _c.length)) return [3 /*break*/, 4];
_d = _c[_b], pattern = _d[0], eventIds = _d[1];
regex = getOrCompileRegex(pattern);
if (regex === null) {
// Pattern was unsafe or invalid - skip (warning already logged)
return [3 /*break*/, 3];
}
return [4 /*yield*/, testRegexWithTimeout(regex, value, 1000)];
case 2:
matched = _e.sent();
if (!matched) {
addIdsToSet(eventIds, failedIds);
}
_e.label = 3;
case 3:
_b++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/];
}
});
});
}
/**
* Checks min/max range constraint.
*/
function checkMinMaxRanges(value, minMaxRanges, failedIds) {
if (typeof value !== "number") {
for (var _i = 0, _a = Object.values(minMaxRanges); _i < _a.length; _i++) {
var eventIds = _a[_i];
addIdsToSet(eventIds, failedIds);
}
return;
}
if (Number.isNaN(value)) {
console.warn("[Avo Inspector] NaN value fails min/max constraint");
for (var _b = 0, _c = Object.values(minMaxRanges); _b < _c.length; _b++) {
var eventIds = _c[_b];
addIdsToSet(eventIds, failedIds);
}
return;
}
for (var _d = 0, _e = Object.entries(minMaxRanges); _d < _e.length; _d++) {
var _f = _e[_d], rangeStr = _f[0], eventIds = _f[1];
var _g = rangeStr.split(","), minStr = _g[0], maxStr = _g[1];
var hasMin = minStr !== "" && minStr !== undefined;
var hasMax = maxStr !== "" && maxStr !== undefined;
var min = hasMin ? parseFloat(minStr) : -Infinity;
var max = hasMax ? parseFloat(maxStr) : Infinity;
if ((hasMin && isNaN(min)) || (hasMax && isNaN(max))) {
console.warn("[Avo Inspector] Invalid min/max range: ".concat(rangeStr));
continue;
}
if (value < min || value > max) {
addIdsToSet(eventIds, failedIds);
}
}
}