UNPKG

@zendesk/retrace

Version:

define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API

199 lines 7.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.withName = withName; exports.withLabel = withLabel; exports.withPerformanceEntryName = withPerformanceEntryName; exports.withType = withType; exports.withStatus = withStatus; exports.withAttributes = withAttributes; exports.withMatchingRelations = withMatchingRelations; exports.withOccurrence = withOccurrence; exports.withComponentRenderCount = withComponentRenderCount; exports.whenIdle = whenIdle; exports.continueWithErrorStatus = continueWithErrorStatus; exports.withAllConditions = withAllConditions; exports.withOneOfConditions = withOneOfConditions; exports.not = not; exports.fromDefinition = fromDefinition; /** * The common name of the span to match. Can be a string, RegExp, or function. */ function withName(value) { return ({ span }, { input: { relatedTo } }) => { if (typeof value === 'string') return span.name === value; if (value instanceof RegExp) return value.test(span.name); return value(span.name, relatedTo); }; } // DRAFT TODO: make test case if one doesnt exist yet // withName((name, relatedTo) => !relatedTo ? false : name === `OmniLog/${relatedTo.ticketId}`) function withLabel(value) { return ({ annotation }) => annotation.labels?.includes(value) ?? false; } /** * The PerformanceEntry.name of the entry to match. Can be a string, RegExp, or function. */ function withPerformanceEntryName(value) { return ({ span }, { input: { relatedTo } }) => { const entryName = span.performanceEntry?.name; if (!entryName) return false; if (typeof value === 'string') return entryName === value; if (value instanceof RegExp) return value.test(entryName); return value(entryName, relatedTo); }; } function withType(value) { return ({ span }) => span.type === value; } function withStatus(value) { return ({ span }) => span.status === value; } /** * The subset of attributes (metadata) to match against the span. */ function withAttributes(attrs) { return ({ span }) => { if (!span.attributes) return false; return Object.entries(attrs).every(([key, value]) => span.attributes[key] === value); }; } /** * A list of keys of trace's relations to match against the span's. */ function withMatchingRelations(keys = true) { return ({ span }, { input: { relatedTo: r }, definition: { relationSchema } }) => { // DRAFT TODO: add test case when relatedTo is missing // if the relatedTo isn't set on the trace yet, we can't match against it, so we return early // similarly, if the span doesn't have any relatedTo set const relatedToInput = r; if (!span.relatedTo || !relatedToInput) return false; const spanRelatedTo = span.relatedTo; const resolvedKeys = typeof keys === 'boolean' && keys ? Object.keys(relationSchema) : keys; if (!resolvedKeys) return false; return resolvedKeys.every((key) => key in spanRelatedTo && spanRelatedTo[key] === relatedToInput[key]); }; } /** * The occurrence of the span with the same name within the operation. */ function withOccurrence(value) { return ({ annotation }) => { if (typeof value === 'number') return annotation.occurrence === value; return value(annotation.occurrence); }; } function withComponentRenderCount(name, renderCount) { return ({ span }) => { if (!('renderCount' in span)) return false; return span.name === name && span.renderCount === renderCount; }; } /** * only applicable for component-lifecycle entries */ function whenIdle(value = true) { const matcherFn = ({ span }) => ('isIdle' in span ? span.isIdle === value : false); return Object.assign(matcherFn, // add a tag to the function if set to true value ? { idleCheck: value } : {}); } /** * Only applicable for 'requiredSpans' list: it will opt-out of the default behavior, * which interrupts the trace if the requiredSpan has an error status. */ function continueWithErrorStatus() { return Object.assign(() => true, // add a tag to the function if set to true { continueWithErrorStatus: true }); } // logical combinators: // AND function withAllConditions(...matchers) { const tags = {}; const definition = {}; for (const matcher of matchers) { // carry over tags from sub-matchers Object.assign(tags, matcher); if (matcher.fromDefinition) { // carry over definition from sub-matchers Object.assign(definition, matcher.fromDefinition); } } const matcherFn = (...args) => matchers.every((matcher) => matcher(...args)); return Object.assign(matcherFn, tags, { fromDefinition: definition }); } // OR function withOneOfConditions(...matchers) { const tags = {}; for (const matcher of matchers) { // carry over tags from sub-matchers Object.assign(tags, matcher); } const matcherFn = (...args) => matchers.some((matcher) => matcher(...args)); return Object.assign(matcherFn, tags, { fromDefinition: { oneOf: matchers } }); } function not(matcher) { // since not is a negation, we don't carry over tags return (...args) => !matcher(...args); } function fromDefinition(definition) { const matchers = []; if (definition.name) { matchers.push(withName(definition.name)); } if (definition.performanceEntryName) { matchers.push(withPerformanceEntryName(definition.performanceEntryName)); } if (definition.type) { matchers.push(withType(definition.type)); } if (definition.status) { matchers.push(withStatus(definition.status)); } if (definition.attributes) { matchers.push(withAttributes(definition.attributes)); } if (definition.matchingRelations) { matchers.push(withMatchingRelations(definition.matchingRelations)); } if (definition.occurrence) { matchers.push(withOccurrence(definition.occurrence)); } if (definition.isIdle) { matchers.push(whenIdle(definition.isIdle)); } if (definition.label) { matchers.push(withLabel(definition.label)); } if (definition.fn) { matchers.push(definition.fn); } let combined; // Check if definition has oneOf property if (definition.oneOf) { // Convert each definition in oneOf array to a matcher and combine with OR const oneOfMatchers = definition.oneOf.map((def) => fromDefinition(def)); combined = withAllConditions(...matchers, withOneOfConditions(...oneOfMatchers)); } else { combined = withAllConditions(...matchers); } combined.fromDefinition = definition; if (typeof definition.matchingIndex === 'number') { combined.matchingIndex = definition.matchingIndex; } return combined; } //# sourceMappingURL=matchSpan.js.map