import { hasExpressionMapping } from '@/utils'; import type { Resolvable, Segment } from '@/types/expressions'; export function createExpressionTelemetryPayload( segments: Segment[], value: string, workflowId: string, sessionId: string, activeNodeType: string, eventSource = 'ndv', ) { const resolvables = segments.filter((s): s is Resolvable => s.kind === 'resolvable'); const erroringResolvables = resolvables.filter((r) => r.error); return { empty_expression: value === '=' || value === '={{}}' || !value, workflow_id: workflowId, source: eventSource, session_id: sessionId, is_transforming_data: resolvables.some((r) => isTransformingData(r.resolvable)), has_parameter: value.includes('$parameter'), has_mapping: hasExpressionMapping(value), node_type: activeNodeType, handlebar_count: resolvables.length, handlebar_error_count: erroringResolvables.length, short_errors: erroringResolvables.map((r) => r.resolved ?? null), full_errors: erroringResolvables.map((erroringResolvable) => { if (!erroringResolvable.fullError) return null; return { ...exposeErrorProperties(erroringResolvable.fullError), stack: erroringResolvable.fullError.stack, }; }), }; } /** * Whether the resolvable is transforming data from another node, * i.e. operating on `$input()`, `$json`, `$()` or `$node[]`. * * ``` * $input.all(). * $input.first(). * $input.last(). * * $json['field']. * $json["field"]. * $json.field'. * * $('nodeName').all(). * $('nodeName').first(). * $('nodeName').last(). * * $("nodeName").all(). * $("nodeName").first(). * $("nodeName").last(). * * $node['nodeName'].all(). * $node['nodeName'].first(). * $node['nodeName'].last(). * * $node["nodeName"].all(). * $node["nodeName"].first(). * $node["nodeName"].last(). * ``` */ function isTransformingData(resolvable: string) { const regex = /(\$input\.\w+\(\)\.|\$json(\[('|")|\.)\w+('|")]?\.|\$\(('|")\w+('|")\)\.\w+\(\)\.|\$node\[('|")\w+('|")\]\.\w+\(\)\.)/; return regex.test(resolvable); } function exposeErrorProperties(error: Error) { return Object.getOwnPropertyNames(error).reduce>((acc, key) => { return (acc[key] = error[key as keyof Error]), acc; }, {}); }