{"version":3,"file":"formula-spec.cjs","names":[],"sources":["../src/formula-spec.ts"],"sourcesContent":["export interface FunctionSpec {\n  name: string;\n  description: string;\n  signature: string;\n  returnType: 'string' | 'number' | 'boolean' | 'any';\n  examples: string[];\n}\n\nexport interface FormulaSpec {\n  version: string;\n  description: string;\n  syntax: {\n    fieldReferences: string[];\n    arithmeticOperators: { operator: string; description: string }[];\n    comparisonOperators: { operator: string; description: string }[];\n    logicalOperators: { operator: string; description: string }[];\n    other: string[];\n  };\n  functions: {\n    string: FunctionSpec[];\n    numeric: FunctionSpec[];\n    boolean: FunctionSpec[];\n    array: FunctionSpec[];\n    conversion: FunctionSpec[];\n    conditional: FunctionSpec[];\n  };\n  features: {\n    name: string;\n    description: string;\n    minVersion: string;\n    examples: string[];\n    dependenciesExtracted?: string[];\n  }[];\n  versionDetection: { feature: string; minVersion: string }[];\n  parseResult: {\n    description: string;\n    interface: string;\n  };\n  astUtilities: {\n    name: string;\n    description: string;\n    signature: string;\n    code: string;\n  }[];\n  examples: {\n    expression: string;\n    description: string;\n    result?: string;\n  }[];\n  apiExamples: {\n    name: string;\n    description: string;\n    code: string;\n  }[];\n  schemaUsage: {\n    structure: string;\n    fieldTypes: string[];\n    rules: string[];\n  };\n}\n\nexport const formulaSpec: FormulaSpec = {\n  version: '1.2',\n  description:\n    'Formula expressions for computed fields. Formulas reference other fields and calculate values automatically.',\n\n  syntax: {\n    fieldReferences: [\n      'Simple field: fieldName (e.g., price, quantity)',\n      'Nested path: object.property (e.g., stats.damage)',\n      'Array index: array[0] or array[-1] for last element',\n      'Bracket notation: [\"field-name\"] for field names containing hyphens',\n      '  - Required when field name contains hyphen (-)',\n      '  - Without brackets: field-name is parsed as \"field minus name\"',\n      '  - With brackets: [\"field-name\"] references the field correctly',\n      'Combined: items[0].price, user.addresses[-1].city, obj[\"field-name\"].value',\n    ],\n    arithmeticOperators: [\n      { operator: '+', description: 'Addition or string concatenation' },\n      { operator: '-', description: 'Subtraction' },\n      { operator: '*', description: 'Multiplication' },\n      { operator: '/', description: 'Division' },\n      { operator: '%', description: 'Modulo (remainder)' },\n    ],\n    comparisonOperators: [\n      { operator: '==', description: 'Equal' },\n      { operator: '!=', description: 'Not equal' },\n      { operator: '>', description: 'Greater than' },\n      { operator: '<', description: 'Less than' },\n      { operator: '>=', description: 'Greater or equal' },\n      { operator: '<=', description: 'Less or equal' },\n    ],\n    logicalOperators: [\n      { operator: '&&', description: 'Logical AND' },\n      { operator: '||', description: 'Logical OR' },\n      { operator: '!', description: 'Logical NOT' },\n    ],\n    other: ['Parentheses: (a + b) * c', 'Unary minus: -value, a + -b'],\n  },\n\n  functions: {\n    string: [\n      {\n        name: 'concat',\n        description: 'Concatenate multiple values into a single string',\n        signature: 'concat(value1, value2, ...)',\n        returnType: 'string',\n        examples: [\n          'concat(firstName, \" \", lastName) // \"John Doe\"',\n          'concat(\"Price: \", price, \" USD\") // \"Price: 100 USD\"',\n        ],\n      },\n      {\n        name: 'upper',\n        description: 'Convert string to uppercase',\n        signature: 'upper(text)',\n        returnType: 'string',\n        examples: ['upper(name) // \"HELLO\"'],\n      },\n      {\n        name: 'lower',\n        description: 'Convert string to lowercase',\n        signature: 'lower(text)',\n        returnType: 'string',\n        examples: ['lower(name) // \"hello\"'],\n      },\n      {\n        name: 'trim',\n        description: 'Remove whitespace from both ends of a string',\n        signature: 'trim(text)',\n        returnType: 'string',\n        examples: ['trim(name) // \"hello\" from \"  hello  \"'],\n      },\n      {\n        name: 'left',\n        description: 'Extract characters from the beginning of a string',\n        signature: 'left(text, count)',\n        returnType: 'string',\n        examples: ['left(name, 3) // \"hel\" from \"hello\"'],\n      },\n      {\n        name: 'right',\n        description: 'Extract characters from the end of a string',\n        signature: 'right(text, count)',\n        returnType: 'string',\n        examples: ['right(name, 3) // \"llo\" from \"hello\"'],\n      },\n      {\n        name: 'replace',\n        description: 'Replace first occurrence of a substring',\n        signature: 'replace(text, search, replacement)',\n        returnType: 'string',\n        examples: ['replace(name, \"o\", \"0\") // \"hell0\" from \"hello\"'],\n      },\n      {\n        name: 'join',\n        description: 'Join array elements into a string',\n        signature: 'join(array, separator?)',\n        returnType: 'string',\n        examples: ['join(tags) // \"a,b,c\"', 'join(tags, \" | \") // \"a | b | c\"'],\n      },\n    ],\n    numeric: [\n      {\n        name: 'round',\n        description: 'Round a number to specified decimal places',\n        signature: 'round(number, decimals?)',\n        returnType: 'number',\n        examples: ['round(3.14159, 2) // 3.14', 'round(3.5) // 4'],\n      },\n      {\n        name: 'floor',\n        description: 'Round down to the nearest integer',\n        signature: 'floor(number)',\n        returnType: 'number',\n        examples: ['floor(3.7) // 3'],\n      },\n      {\n        name: 'ceil',\n        description: 'Round up to the nearest integer',\n        signature: 'ceil(number)',\n        returnType: 'number',\n        examples: ['ceil(3.2) // 4'],\n      },\n      {\n        name: 'abs',\n        description: 'Get the absolute value',\n        signature: 'abs(number)',\n        returnType: 'number',\n        examples: ['abs(-5) // 5'],\n      },\n      {\n        name: 'sqrt',\n        description: 'Calculate the square root',\n        signature: 'sqrt(number)',\n        returnType: 'number',\n        examples: ['sqrt(16) // 4'],\n      },\n      {\n        name: 'pow',\n        description: 'Raise a number to a power',\n        signature: 'pow(base, exponent)',\n        returnType: 'number',\n        examples: ['pow(2, 3) // 8'],\n      },\n      {\n        name: 'min',\n        description: 'Get the minimum of multiple values',\n        signature: 'min(value1, value2, ...)',\n        returnType: 'number',\n        examples: ['min(a, b, c) // smallest value'],\n      },\n      {\n        name: 'max',\n        description: 'Get the maximum of multiple values',\n        signature: 'max(value1, value2, ...)',\n        returnType: 'number',\n        examples: ['max(a, b, c) // largest value'],\n      },\n      {\n        name: 'log',\n        description: 'Calculate the natural logarithm',\n        signature: 'log(number)',\n        returnType: 'number',\n        examples: ['log(10) // 2.302...'],\n      },\n      {\n        name: 'log10',\n        description: 'Calculate the base-10 logarithm',\n        signature: 'log10(number)',\n        returnType: 'number',\n        examples: ['log10(100) // 2'],\n      },\n      {\n        name: 'exp',\n        description: 'Calculate e raised to a power',\n        signature: 'exp(number)',\n        returnType: 'number',\n        examples: ['exp(1) // 2.718...'],\n      },\n      {\n        name: 'sign',\n        description: 'Get the sign of a number (-1, 0, or 1)',\n        signature: 'sign(number)',\n        returnType: 'number',\n        examples: ['sign(-5) // -1', 'sign(0) // 0', 'sign(5) // 1'],\n      },\n      {\n        name: 'length',\n        description: 'Get the length of a string or array',\n        signature: 'length(value)',\n        returnType: 'number',\n        examples: ['length(name) // 5 from \"hello\"', 'length(items) // 3'],\n      },\n    ],\n    boolean: [\n      {\n        name: 'and',\n        description: 'Logical AND of two values',\n        signature: 'and(a, b)',\n        returnType: 'boolean',\n        examples: ['and(isActive, hasPermission) // true if both true'],\n      },\n      {\n        name: 'or',\n        description: 'Logical OR of two values',\n        signature: 'or(a, b)',\n        returnType: 'boolean',\n        examples: ['or(isAdmin, isOwner) // true if either true'],\n      },\n      {\n        name: 'not',\n        description: 'Logical NOT of a value',\n        signature: 'not(value)',\n        returnType: 'boolean',\n        examples: ['not(isDeleted) // true if false'],\n      },\n      {\n        name: 'contains',\n        description: 'Check if a string contains a substring',\n        signature: 'contains(text, search)',\n        returnType: 'boolean',\n        examples: ['contains(name, \"ell\") // true for \"hello\"'],\n      },\n      {\n        name: 'startswith',\n        description: 'Check if a string starts with a prefix',\n        signature: 'startswith(text, prefix)',\n        returnType: 'boolean',\n        examples: ['startswith(name, \"hel\") // true for \"hello\"'],\n      },\n      {\n        name: 'endswith',\n        description: 'Check if a string ends with a suffix',\n        signature: 'endswith(text, suffix)',\n        returnType: 'boolean',\n        examples: ['endswith(name, \"llo\") // true for \"hello\"'],\n      },\n      {\n        name: 'isnull',\n        description: 'Check if a value is null or undefined',\n        signature: 'isnull(value)',\n        returnType: 'boolean',\n        examples: ['isnull(optionalField) // true if null/undefined'],\n      },\n      {\n        name: 'includes',\n        description: 'Check if an array contains a value',\n        signature: 'includes(array, value)',\n        returnType: 'boolean',\n        examples: [\n          'includes(tags, \"featured\") // true if array contains value',\n        ],\n      },\n    ],\n    array: [\n      {\n        name: 'sum',\n        description:\n          'Calculate the sum of array elements. Supports wildcard property access to sum nested values.',\n        signature: 'sum(array)',\n        returnType: 'number',\n        examples: [\n          'sum(prices) // total of all prices',\n          'sum(items[*].price) // sum prices from array of objects',\n          'sum(orders[*].items[*].amount) // sum nested arrays',\n        ],\n      },\n      {\n        name: 'avg',\n        description:\n          'Calculate the average of array elements. Supports wildcard property access.',\n        signature: 'avg(array)',\n        returnType: 'number',\n        examples: [\n          'avg(scores) // average score',\n          'avg(items[*].rating) // average rating from array of objects',\n        ],\n      },\n      {\n        name: 'count',\n        description: 'Get the number of elements in an array',\n        signature: 'count(array)',\n        returnType: 'number',\n        examples: ['count(items) // number of items'],\n      },\n      {\n        name: 'first',\n        description: 'Get the first element of an array',\n        signature: 'first(array)',\n        returnType: 'any',\n        examples: ['first(items) // first item'],\n      },\n      {\n        name: 'last',\n        description: 'Get the last element of an array',\n        signature: 'last(array)',\n        returnType: 'any',\n        examples: ['last(items) // last item'],\n      },\n    ],\n    conversion: [\n      {\n        name: 'tostring',\n        description: 'Convert a value to string',\n        signature: 'tostring(value)',\n        returnType: 'string',\n        examples: ['tostring(42) // \"42\"'],\n      },\n      {\n        name: 'tonumber',\n        description: 'Convert a value to number',\n        signature: 'tonumber(value)',\n        returnType: 'number',\n        examples: ['tonumber(\"42\") // 42'],\n      },\n      {\n        name: 'toboolean',\n        description: 'Convert a value to boolean',\n        signature: 'toboolean(value)',\n        returnType: 'boolean',\n        examples: ['toboolean(1) // true', 'toboolean(0) // false'],\n      },\n    ],\n    conditional: [\n      {\n        name: 'if',\n        description: 'Return one of two values based on a condition',\n        signature: 'if(condition, valueIfTrue, valueIfFalse)',\n        returnType: 'any',\n        examples: [\n          'if(stock > 0, \"Available\", \"Out of Stock\")',\n          'if(price > 100, price * 0.9, price)',\n        ],\n      },\n      {\n        name: 'coalesce',\n        description: 'Return the first non-null value',\n        signature: 'coalesce(value1, value2, ...)',\n        returnType: 'any',\n        examples: ['coalesce(nickname, name, \"Anonymous\")'],\n      },\n    ],\n  },\n\n  features: [\n    {\n      name: 'simple_refs',\n      description: 'Reference top-level fields by name',\n      minVersion: '1.0',\n      examples: ['price', 'quantity', 'baseDamage'],\n      dependenciesExtracted: ['[\"price\"]', '[\"quantity\"]', '[\"baseDamage\"]'],\n    },\n    {\n      name: 'arithmetic',\n      description: 'Basic math operations (+, -, *, /)',\n      minVersion: '1.0',\n      examples: ['price * 1.1', 'a + b - c', 'quantity * price'],\n    },\n    {\n      name: 'comparison',\n      description: 'Compare values (>, <, >=, <=, ==, !=)',\n      minVersion: '1.0',\n      examples: ['price > 100', 'x == 10', 'quantity >= 5'],\n    },\n    {\n      name: 'nested_path',\n      description: 'Access nested object properties using dot notation',\n      minVersion: '1.1',\n      examples: ['stats.damage', 'user.profile.name', 'item.metadata.category'],\n      dependenciesExtracted: ['[\"stats.damage\"]'],\n    },\n    {\n      name: 'array_index',\n      description:\n        'Access array elements by numeric index. Negative indices access from the end',\n      minVersion: '1.1',\n      examples: [\n        'items[0].price',\n        'inventory[1].quantity',\n        'items[-1].name  // last element',\n        'items[-2].price // second to last',\n      ],\n      dependenciesExtracted: ['[\"items[0].price\"]', '[\"items[-1].name\"]'],\n    },\n    {\n      name: 'array_wildcard_property',\n      description:\n        'Access properties across all array elements using [*] wildcard. Property access after wildcard maps over all elements. Multiple wildcards flatten nested arrays.',\n      minVersion: '1.1',\n      examples: [\n        'items[*].price                    // [10, 20, 30] - map property',\n        'sum(items[*].price)               // 60 - sum mapped values',\n        'avg(items[*].rating)              // average of all ratings',\n        'values[*].nested.value            // deeply nested property access',\n        'orders[*].items                   // [[1,2], [3,4]] - array of arrays',\n        'orders[*].items[*]                // [1,2,3,4] - flattened',\n        'sum(orders[*].items[*].amount)    // sum all nested amounts',\n      ],\n      dependenciesExtracted: [\n        '[\"items[*].price\"]',\n        '[\"orders[*].items[*].amount\"]',\n      ],\n    },\n    {\n      name: 'root_path',\n      description:\n        'Absolute path reference starting with /. Always resolves from root data, even inside array item formulas',\n      minVersion: '1.1',\n      examples: [\n        '/taxRate',\n        '/config.tax',\n        'price * (1 + /taxRate)',\n        'price * /config.multiplier',\n      ],\n      dependenciesExtracted: ['[\"/taxRate\"]', '[\"/config.tax\"]'],\n    },\n    {\n      name: 'relative_path',\n      description:\n        'Relative path reference starting with ../. Each ../ goes up one level in the path hierarchy. Works with nested objects, arrays, and combinations. Supports accessing nested properties after the relative prefix (e.g., ../config.value)',\n      minVersion: '1.1',\n      examples: [\n        '../discount',\n        '../../rootRate',\n        '../config.multiplier',\n        'price * (1 - ../discount)',\n        'price * ../../globalRate',\n        'price * ../settings.tax.rate',\n      ],\n      dependenciesExtracted: [\n        '[\"../discount\"]',\n        '[\"../../rootRate\"]',\n        '[\"../config.multiplier\"]',\n      ],\n    },\n    {\n      name: 'function_named_fields',\n      description:\n        'Fields can have the same name as built-in functions (max, min, sum, etc.). Built-in functions are always checked first when a function call is made',\n      minVersion: '1.0',\n      examples: [\n        'max(max, 0)',\n        'min(min, 100)',\n        'max(max - field.min, 0)',\n        'round(round * 2)',\n      ],\n    },\n    {\n      name: 'bracket_notation',\n      description:\n        'Access fields containing hyphens using bracket notation with quotes. Required because \"field-name\" would be parsed as \"field minus name\" (subtraction). Works like JavaScript object[\"key\"] syntax.',\n      minVersion: '1.1',\n      examples: [\n        '[\"field-name\"]          // Without brackets: field - name (subtraction!)',\n        \"['field-name']          // Single quotes also work\",\n        '[\"field-one\"][\"field-two\"]',\n        'obj[\"field-name\"].value',\n        '[\"items-list\"][0][\"val\"]',\n        '[\"price-new\"] * 2',\n      ],\n      dependenciesExtracted: ['[\"field-name\"]', \"['field-name']\"],\n    },\n    {\n      name: 'context_token',\n      description:\n        'Array context tokens provide information about current position and neighboring elements when evaluating formulas inside arrays. # prefix for scalar metadata (number/boolean), @ prefix for object references.',\n      minVersion: '1.2',\n      examples: [\n        '#index                  // Current array index (0-based)',\n        '#length                 // Array length',\n        '#first                  // true if first element',\n        '#last                   // true if last element',\n        '@prev                   // Previous element (null if first)',\n        '@next                   // Next element (null if last)',\n        '#parent.index           // Index in parent array (nested arrays)',\n        '#parent.length          // Length of parent array',\n        '#root.index             // Index in topmost array',\n        '@root.prev              // Previous element in topmost array',\n        'if(#first, value, @prev.total + value)  // Running total',\n        'concat(#parent.index + 1, \".\", #index + 1)  // \"1.1\", \"1.2\" numbering',\n      ],\n    },\n  ],\n\n  versionDetection: [\n    { feature: 'Simple refs, arithmetic, comparisons', minVersion: '1.0' },\n    { feature: 'Function-named fields (max(max, 0))', minVersion: '1.0' },\n    { feature: 'Nested paths (a.b)', minVersion: '1.1' },\n    { feature: 'Array index ([0], [-1])', minVersion: '1.1' },\n    { feature: 'Array wildcard property (items[*].price)', minVersion: '1.1' },\n    { feature: 'Absolute paths (/field)', minVersion: '1.1' },\n    { feature: 'Relative paths (../field)', minVersion: '1.1' },\n    { feature: 'Bracket notation ([\"field-name\"])', minVersion: '1.1' },\n    { feature: 'Context tokens (#index, @prev)', minVersion: '1.2' },\n  ],\n\n  parseResult: {\n    description:\n      'The parser automatically detects the minimum required version',\n    interface: `interface ParseResult {\n  ast: ASTNode;           // Abstract syntax tree\n  dependencies: string[]; // List of field dependencies\n  features: string[];     // List of detected features\n  minVersion: string;     // Minimum required version (\"1.0\" or \"1.1\")\n}`,\n  },\n\n  astUtilities: [\n    {\n      name: 'serializeAst',\n      description:\n        'Convert an AST back to a formula string. Useful for debugging or displaying parsed formulas.',\n      signature: 'serializeAst(ast: ASTNode): string',\n      code: `import { parseFormula, serializeAst } from '@revisium/formula';\n\nconst { ast } = parseFormula('price * (1 + taxRate)');\nserializeAst(ast)\n// \"price * (1 + taxRate)\"\n\n// After modifying AST nodes, serialize back to string\nconst { ast: ast2 } = parseFormula('a + b');\nserializeAst(ast2)\n// \"a + b\"`,\n    },\n    {\n      name: 'replaceDependencies',\n      description:\n        'Replace field references in an AST with new names. Useful for renaming fields or migrating formulas.',\n      signature:\n        'replaceDependencies(ast: ASTNode, replacements: Record<string, string>): ASTNode',\n      code: `import { parseFormula, replaceDependencies, serializeAst } from '@revisium/formula';\n\n// Rename a field in a formula\nconst { ast } = parseFormula('oldPrice * quantity');\nconst newAst = replaceDependencies(ast, { oldPrice: 'price' });\nserializeAst(newAst)\n// \"price * quantity\"\n\n// Rename multiple fields\nconst { ast: ast2 } = parseFormula('a + b * c');\nconst newAst2 = replaceDependencies(ast2, { a: 'x', b: 'y', c: 'z' });\nserializeAst(newAst2)\n// \"x + y * z\"\n\n// Works with nested paths\nconst { ast: ast3 } = parseFormula('stats.damage * multiplier');\nconst newAst3 = replaceDependencies(ast3, { 'stats.damage': 'stats.power' });\nserializeAst(newAst3)\n// \"stats.power * multiplier\"`,\n    },\n  ],\n\n  examples: [\n    {\n      expression: 'price * quantity',\n      description: 'Calculate total from price and quantity',\n      result: 'number',\n    },\n    {\n      expression: 'firstName + \" \" + lastName',\n      description: 'Concatenate strings with space',\n      result: 'string',\n    },\n    {\n      expression: 'quantity > 0',\n      description: 'Check if in stock',\n      result: 'boolean',\n    },\n    {\n      expression: 'if(stock > 0, \"Available\", \"Out of Stock\")',\n      description: 'Conditional text based on stock',\n      result: 'string',\n    },\n    {\n      expression: 'price * (1 + taxRate)',\n      description: 'Price with tax',\n      result: 'number',\n    },\n    {\n      expression: 'items[0].price + items[1].price',\n      description: 'Sum first two item prices (v1.1)',\n      result: 'number',\n    },\n  ],\n\n  apiExamples: [\n    {\n      name: 'Simple Expression (v1.0)',\n      description: 'Parse a basic arithmetic expression',\n      code: `parseExpression('price * 1.1')\n// {\n//   minVersion: \"1.0\",\n//   features: [],\n//   dependencies: [\"price\"]\n// }`,\n    },\n    {\n      name: 'Nested Path (v1.1)',\n      description: 'Parse expression with nested object access',\n      code: `parseExpression('stats.damage * multiplier')\n// {\n//   minVersion: \"1.1\",\n//   features: [\"nested_path\"],\n//   dependencies: [\"stats.damage\", \"multiplier\"]\n// }`,\n    },\n    {\n      name: 'Array Access (v1.1)',\n      description: 'Parse expression with array index access',\n      code: `parseExpression('items[0].price + items[1].price')\n// {\n//   minVersion: \"1.1\",\n//   features: [\"array_index\", \"nested_path\"],\n//   dependencies: [\"items[0].price\", \"items[1].price\"]\n// }`,\n    },\n    {\n      name: 'Evaluate expressions',\n      description: 'Execute formulas with context data',\n      code: `evaluate('price * 1.1', { price: 100 })\n// 110\n\nevaluate('stats.damage', { stats: { damage: 50 } })\n// 50\n\nevaluate('items[0].price', { items: [{ price: 10 }] })\n// 10\n\nevaluate('price > 100', { price: 150 })\n// true\n\nevaluate('a + b * c', { a: 1, b: 2, c: 3 })\n// 7`,\n    },\n    {\n      name: 'Function-named fields',\n      description: 'Fields can have the same name as built-in functions',\n      code: `// Built-in functions take precedence in function calls\nevaluate('max(max, 0)', { max: 10 })\n// 10 (max() function, then max field)\n\nevaluate('max(max - field.min, 0)', { max: 100, field: { min: 20 } })\n// 80\n\nevaluate('round(round * 2)', { round: 3.7 })\n// 7\n\n// Field named \"sum\" doesn't conflict with sum() function\nevaluate('sum(values) + sum', { values: [1, 2, 3], sum: 10 })\n// 16`,\n    },\n    {\n      name: 'Evaluate with context (array items)',\n      description:\n        'Use evaluateWithContext() for array item formulas with path resolution',\n      code: `// Absolute path: /field always resolves from root\nevaluateWithContext('price * (1 + /taxRate)', {\n  rootData: { taxRate: 0.1, items: [{ price: 100 }] },\n  itemData: { price: 100 },\n  currentPath: 'items[0]'\n})\n// 110\n\n// Nested absolute path\nevaluateWithContext('price * /config.multiplier', {\n  rootData: { config: { multiplier: 1.5 }, items: [] },\n  itemData: { price: 100 },\n  currentPath: 'items[0]'\n})\n// 150\n\n// Relative path: ../field resolves from parent (root)\nevaluateWithContext('price * (1 - ../discount)', {\n  rootData: { discount: 0.2, items: [] },\n  itemData: { price: 100 },\n  currentPath: 'items[0]'\n})\n// 80\n\n// itemData takes precedence over rootData for same field\nevaluateWithContext('value + 10', {\n  rootData: { value: 100 },\n  itemData: { value: 50 },\n  currentPath: 'items[0]'\n})\n// 60`,\n    },\n    {\n      name: 'Relative paths - path resolution',\n      description:\n        'Understanding how ../ resolves based on currentPath. Each ../ goes up one segment (object property or array element counts as one segment)',\n      code: `// Path structure explanation:\n// currentPath splits by \".\" (dots), keeping array indices attached to field names\n// \"items[0]\" = 1 segment\n// \"items[0].inner\" = 2 segments: [\"items[0]\", \"inner\"]\n// \"container.items[0]\" = 2 segments: [\"container\", \"items[0]\"]\n\n// Single ../ from array item -> goes to root\n// currentPath: \"items[0]\" (1 segment)\n// ../ goes up 1 level -> root\nevaluateWithContext('price * ../discount', {\n  rootData: { discount: 0.2, items: [{ price: 100 }] },\n  itemData: { price: 100 },\n  currentPath: 'items[0]'\n})\n// Resolves ../discount to root.discount = 0.2\n// Result: 100 * 0.2 = 20\n\n// Single ../ from nested object in array -> goes to array item\n// currentPath: \"items[0].inner\" (2 segments)\n// ../ goes up 1 level -> \"items[0]\"\nevaluateWithContext('price * ../itemMultiplier', {\n  rootData: { items: [{ itemMultiplier: 3, inner: { price: 10 } }] },\n  itemData: { price: 10 },\n  currentPath: 'items[0].inner'\n})\n// Resolves ../itemMultiplier to items[0].itemMultiplier = 3\n// Result: 10 * 3 = 30\n\n// Double ../../ from nested object in array -> goes to root\n// currentPath: \"items[0].inner\" (2 segments)\n// ../../ goes up 2 levels -> root\nevaluateWithContext('price * ../../rootRate', {\n  rootData: { rootRate: 2, items: [{ inner: { price: 5 } }] },\n  itemData: { price: 5 },\n  currentPath: 'items[0].inner'\n})\n// Resolves ../../rootRate to root.rootRate = 2\n// Result: 5 * 2 = 10`,\n    },\n    {\n      name: 'Relative paths - nested arrays',\n      description:\n        'How relative paths work with arrays inside objects and nested arrays',\n      code: `// Array inside nested object\n// currentPath: \"container.items[0]\" (2 segments: [\"container\", \"items[0]\"])\n// ../ goes up 1 level -> \"container\"\nevaluateWithContext('price * ../containerRate', {\n  rootData: {\n    container: {\n      containerRate: 4,\n      items: [{ price: 5 }]\n    }\n  },\n  itemData: { price: 5 },\n  currentPath: 'container.items[0]'\n})\n// Resolves ../containerRate to container.containerRate = 4\n// Result: 5 * 4 = 20\n\n// ../../ from array inside object -> goes to root\n// currentPath: \"container.items[0]\" (2 segments)\n// ../../ goes up 2 levels -> root\nevaluateWithContext('price * ../../rootVal', {\n  rootData: {\n    rootVal: 6,\n    container: { items: [{ price: 5 }] }\n  },\n  itemData: { price: 5 },\n  currentPath: 'container.items[0]'\n})\n// Resolves ../../rootVal to root.rootVal = 6\n// Result: 5 * 6 = 30\n\n// Nested arrays: items[].subItems[]\n// currentPath: \"items[0].subItems[0]\" (2 segments: [\"items[0]\", \"subItems[0]\"])\n// ../ goes up 1 level -> \"items[0]\"\nevaluateWithContext('qty * ../itemPrice', {\n  rootData: {\n    items: [{ itemPrice: 10, subItems: [{ qty: 3 }] }]\n  },\n  itemData: { qty: 3 },\n  currentPath: 'items[0].subItems[0]'\n})\n// Resolves ../itemPrice to items[0].itemPrice = 10\n// Result: 3 * 10 = 30`,\n    },\n    {\n      name: 'Relative paths - accessing nested properties',\n      description:\n        'Relative paths can include nested property access after the ../ prefix',\n      code: `// ../sibling.nested accesses a sibling with nested property\n// currentPath: \"items[0].products[0]\" (2 segments)\n// ../ goes to \"items[0]\", then accesses .config.discount\nevaluateWithContext('price * ../config.discount', {\n  rootData: {\n    items: [{\n      config: { discount: 0.9 },\n      products: [{ price: 100 }]\n    }]\n  },\n  itemData: { price: 100 },\n  currentPath: 'items[0].products[0]'\n})\n// Resolves ../config.discount to items[0].config.discount = 0.9\n// Result: 100 * 0.9 = 90\n\n// Deep nested: ../../settings.tax.rate\nevaluateWithContext('amount * ../../settings.tax.rate', {\n  rootData: {\n    settings: { tax: { rate: 0.1 } },\n    orders: [{ items: [{ amount: 200 }] }]\n  },\n  itemData: { amount: 200 },\n  currentPath: 'orders[0].items[0]'\n})\n// Resolves ../../settings.tax.rate to root.settings.tax.rate = 0.1\n// Result: 200 * 0.1 = 20`,\n    },\n    {\n      name: 'Relative paths - complex nesting',\n      description: 'Complex scenarios with arrays inside objects inside arrays',\n      code: `// Array inside object inside array\n// Structure: items[].container.subItems[]\n// currentPath: \"items[0].container.subItems[0]\" (3 segments)\nevaluateWithContext('val * ../containerMultiplier', {\n  rootData: {\n    items: [{\n      container: {\n        containerMultiplier: 4,\n        subItems: [{ val: 3 }]\n      }\n    }]\n  },\n  itemData: { val: 3 },\n  currentPath: 'items[0].container.subItems[0]'\n})\n// ../ goes to \"items[0].container\"\n// Resolves ../containerMultiplier to items[0].container.containerMultiplier = 4\n// Result: 3 * 4 = 12\n\n// ../../ from same structure -> goes to array item\nevaluateWithContext('val * ../../itemRate', {\n  rootData: {\n    items: [{\n      itemRate: 5,\n      container: { subItems: [{ val: 2 }] }\n    }]\n  },\n  itemData: { val: 2 },\n  currentPath: 'items[0].container.subItems[0]'\n})\n// ../../ goes to \"items[0]\"\n// Resolves ../../itemRate to items[0].itemRate = 5\n// Result: 2 * 5 = 10\n\n// ../../../ from same structure -> goes to root\nevaluateWithContext('val * ../../../rootFactor', {\n  rootData: {\n    rootFactor: 3,\n    items: [{\n      container: { subItems: [{ val: 7 }] }\n    }]\n  },\n  itemData: { val: 7 },\n  currentPath: 'items[0].container.subItems[0]'\n})\n// ../../../ goes to root\n// Resolves ../../../rootFactor to root.rootFactor = 3\n// Result: 7 * 3 = 21`,\n    },\n    {\n      name: 'Array context tokens - basic',\n      description:\n        'Use #index, #length, #first, #last for position info; @prev, @next for neighbor access',\n      code: `// arrayContext provides position info for array item formulas\nconst arrayContext = {\n  levels: [{\n    index: 2,      // current position\n    length: 5,     // array length\n    prev: { value: 20 },  // previous element\n    next: { value: 40 },  // next element\n  }]\n};\n\nevaluateWithContext('#index', { rootData: {}, arrayContext })\n// 2\n\nevaluateWithContext('#length', { rootData: {}, arrayContext })\n// 5\n\nevaluateWithContext('#first', { rootData: {}, arrayContext })\n// false (index !== 0)\n\nevaluateWithContext('#last', { rootData: {}, arrayContext })\n// false (index !== length - 1)\n\nevaluateWithContext('@prev.value', { rootData: {}, arrayContext })\n// 20\n\nevaluateWithContext('@next.value', { rootData: {}, arrayContext })\n// 40\n\n// At first element, @prev is null\nevaluateWithContext('@prev', {\n  rootData: {},\n  arrayContext: { levels: [{ index: 0, length: 3, prev: null, next: {} }] }\n})\n// null`,\n    },\n    {\n      name: 'Array context tokens - nested arrays',\n      description:\n        'Access parent array context with #parent.*, #root.*, @parent.*, @root.*',\n      code: `// For nested arrays like orders[].items[]:\n// levels[0] = innermost (items), levels[1] = parent (orders)\nconst arrayContext = {\n  levels: [\n    { index: 1, length: 3, prev: {}, next: {} },  // items[1]\n    { index: 2, length: 5, prev: {}, next: {} },  // orders[2]\n  ]\n};\n\nevaluateWithContext('#index', { rootData: {}, arrayContext })\n// 1 (current item index)\n\nevaluateWithContext('#parent.index', { rootData: {}, arrayContext })\n// 2 (parent order index)\n\nevaluateWithContext('#parent.length', { rootData: {}, arrayContext })\n// 5 (number of orders)\n\n// #root.* is shortcut for topmost array (same as #parent.* for 2 levels)\nevaluateWithContext('#root.index', { rootData: {}, arrayContext })\n// 2\n\n// For 3+ levels, #root always points to outermost array\nconst threeLevel = {\n  levels: [\n    { index: 0, length: 2, prev: null, next: {} },  // innermost\n    { index: 1, length: 3, prev: {}, next: {} },    // middle\n    { index: 2, length: 4, prev: {}, next: null },  // outermost (root)\n  ]\n};\n\nevaluateWithContext('#parent.parent.index', { rootData: {}, arrayContext: threeLevel })\n// 2 (outermost)\n\nevaluateWithContext('#root.index', { rootData: {}, arrayContext: threeLevel })\n// 2 (same as #parent.parent.index)`,\n    },\n    {\n      name: 'Array context tokens - practical examples',\n      description:\n        'Common patterns: running total, numbering, delta calculation',\n      code: `// Running total pattern (like Excel)\n// rows[].runningTotal = if(#first, value, @prev.runningTotal + value)\nconst rows = [\n  { value: 10 },\n  { value: 20 },\n  { value: 15 },\n];\n\n// For rows[2]:\nevaluateWithContext('if(#first, value, @prev.value + value)', {\n  rootData: {},\n  itemData: { value: 15 },\n  arrayContext: {\n    levels: [{\n      index: 2,\n      length: 3,\n      prev: { value: 20 },  // Note: use non-computed field from prev\n      next: null,\n    }]\n  }\n})\n// 35 (20 + 15)\n\n// Nested numbering like \"1.1\", \"1.2\", \"2.1\"\n// sections[].questions[].number\nevaluateWithContext('concat(#parent.index + 1, \".\", #index + 1)', {\n  rootData: {},\n  arrayContext: {\n    levels: [\n      { index: 1, length: 3, prev: {}, next: {} },  // question index\n      { index: 0, length: 2, prev: null, next: {} }, // section index\n    ]\n  }\n})\n// \"1.2\"\n\n// Delta from previous\n// measurements[].delta = if(#first, 0, value - @prev.value)\nevaluateWithContext('if(#first, 0, value - @prev.value)', {\n  rootData: {},\n  itemData: { value: 105 },\n  arrayContext: {\n    levels: [{\n      index: 1,\n      length: 3,\n      prev: { value: 100 },\n      next: { value: 102 },\n    }]\n  }\n})\n// 5`,\n    },\n  ],\n\n  schemaUsage: {\n    structure:\n      '{ \"x-formula\": { \"version\": 1, \"expression\": \"...\" }, \"readOnly\": true }',\n    fieldTypes: ['string', 'number', 'boolean'],\n    rules: [\n      'Add x-formula to string, number, or boolean field schema',\n      'readOnly: true is REQUIRED for fields with x-formula',\n      'Expression must reference existing fields in the same table',\n      'Circular dependencies are not allowed (a references b, b references a)',\n    ],\n  },\n};\n"],"mappings":"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AY;GACZ,UAAU,CAAC,0CAAwC;GACpD,CACF;EACF;CAED,UAAU;EACR;GACE,MAAM;GACN,aAAa;GACb,YAAY;GACZ,UAAU;IAAC;IAAS;IAAY;IAAa;GAC7C,uBAAuB;IAAC;IAAa;IAAgB;IAAiB;GACvE;EACD;GACE,MAAM;GACN,aAAa;GACb,YAAY;GACZ,UAAU;IAAC;IAAe;IAAa;IAAmB;GAC3D;EACD;GACE,MAAM;GACN,aAAa;GACb,YAAY;GACZ,UAAU;IAAC;IAAe;IAAW;IAAgB;GACtD;EACD;GACE,MAAM;GACN,aAAa;GACb,YAAY;GACZ,UAAU;IAAC;IAAgB;IAAqB;IAAyB;GACzE,uBAAuB,CAAC,qBAAmB;GAC5C;EACD;GACE,MAAM;GACN,aACE;GACF,YAAY;GACZ,UAAU;IACR;IACA;IACA;IACA;IACD;GACD,uBAAuB,CAAC,wBAAsB,uBAAqB;GACpE;EACD;GACE,MAAM;GACN,aACE;GACF,YAAY;GACZ,UAAU;IACR;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,uBAAuB,CACrB,wBACA,kCACD;GACF;EACD;GACE,MAAM;GACN,aACE;GACF,YAAY;GACZ,UAAU;IACR;IACA;IACA;IACA;IACD;GACD,uBAAuB,CAAC,kBAAgB,oBAAkB;GAC3D;EACD;GACE,MAAM;GACN,aACE;GACF,YAAY;GACZ,UAAU;IACR;IACA;IACA;IACA;IACA;IACA;IACD;GACD,uBAAuB;IACrB;IACA;IACA;IACD;GACF;EACD;GACE,MAAM;GACN,aACE;GACF,YAAY;GACZ,UAAU;IACR;IACA;IACA;IACA;IACD;GACF;EACD;GACE,MAAM;GACN,aACE;GACF,YAAY;GACZ,UAAU;IACR;IACA;IACA;IACA;IACA;IACA;IACD;GACD,uBAAuB,CAAC,oBAAkB,iBAAiB;GAC5D;EACD;GACE,MAAM;GACN,aACE;GACF,YAAY;GACZ,UAAU;IACR;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACF;EACF;CAED,kBAAkB;EAChB;GAAE,SAAS;GAAwC,YAAY;GAAO;EACtE;GAAE,SAAS;GAAuC,YAAY;GAAO;EACrE;GAAE,SAAS;GAAsB,YAAY;GAAO;EACpD;GAAE,SAAS;GAA2B,YAAY;GAAO;EACzD;GAAE,SAAS;GAA4C,YAAY;GAAO;EAC1E;GAAE,SAAS;GAA2B,YAAY;GAAO;EACzD;GAAE,SAAS;GAA6B,YAAY;GAAO;EAC3D;GAAE,SAAS;GAAqC,YAAY;GAAO;EACnE;GAAE,SAAS;GAAkC,YAAY;GAAO;EACjE;CAED,aAAa;EACX,aACE;EACF,WAAW;;;;;;EAMZ;CAED,cAAc,CACZ;EACE,MAAM;EACN,aACE;EACF,WAAW;EACX,MAAM;;;;;;;;;;EAUP,EACD;EACE,MAAM;EACN,aACE;EACF,WACE;EACF,MAAM;;;;;;;;;;;;;;;;;;;EAmBP,CACF;CAED,UAAU;EACR;GACE,YAAY;GACZ,aAAa;GACb,QAAQ;GACT;EACD;GACE,YAAY;GACZ,aAAa;GACb,QAAQ;GACT;EACD;GACE,YAAY;GACZ,aAAa;GACb,QAAQ;GACT;EACD;GACE,YAAY;GACZ,aAAa;GACb,QAAQ;GACT;EACD;GACE,YAAY;GACZ,aAAa;GACb,QAAQ;GACT;EACD;GACE,YAAY;GACZ,aAAa;GACb,QAAQ;GACT;EACF;CAED,aAAa;EACX;GACE,MAAM;GACN,aAAa;GACb,MAAM;;;;;;GAMP;EACD;GACE,MAAM;GACN,aAAa;GACb,MAAM;;;;;;GAMP;EACD;GACE,MAAM;GACN,aAAa;GACb,MAAM;;;;;;GAMP;EACD;GACE,MAAM;GACN,aAAa;GACb,MAAM;;;;;;;;;;;;;;GAcP;EACD;GACE,MAAM;GACN,aAAa;GACb,MAAM;;;;;;;;;;;;;GAaP;EACD;GACE,MAAM;GACN,aACE;GACF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BP;EACD;GACE,MAAM;GACN,aACE;GACF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCP;EACD;GACE,MAAM;GACN,aACE;GACF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CP;EACD;GACE,MAAM;GACN,aACE;GACF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BP;EACD;GACE,MAAM;GACN,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDP;EACD;GACE,MAAM;GACN,aACE;GACF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCP;EACD;GACE,MAAM;GACN,aACE;GACF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCP;EACD;GACE,MAAM;GACN,aACE;GACF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDP;EACF;CAED,aAAa;EACX,WACE;EACF,YAAY;GAAC;GAAU;GAAU;GAAU;EAC3C,OAAO;GACL;GACA;GACA;GACA;GACD;EACF;CACF"}