@hamelin.sh/documentation 0.2.9-prerelease.20251024T161808 → 0.2.9

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.
Files changed (2) hide show
  1. package/dist/main.js +2 -1
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -486,6 +486,7 @@ scoring weights without affecting the overall detection logic.`,
486
486
  "function-reference/data-structure-functions.md": "# Data Structure Functions\n\nScalar functions for data structure operations and type information that can be used in any expression context.\n\n## `typeof(x)`\n\nReturns type information for any expression.\n\n### Parameters\n\n- **x** - Expression of any type\n\n### Description\n\nThe `typeof()` function returns a struct containing detailed type information\nabout the input expression. The result includes both the Hamelin type name\nand the corresponding SQL type name. This function is useful for debugging,\ntype introspection, and understanding how Hamelin maps types to the underlying\nSQL engine.\n\n## `map(keys, values)`\n\nCreates a map from separate key and value arrays.\n\n### Parameters\n\n- **keys** - Array expression containing map keys\n- **values** - Array expression containing map values\n\n### Description\n\nThe `map()` function creates a map by pairing elements from the keys array\nwith elements from the values array. Both arrays must have the same length.\nThe nth element from the keys array is paired with the nth element from the\nvalues array. If the arrays have different lengths, an error is raised.\n\n## `map(elements)`\n\nCreates a map from an array of key-value tuples.\n\n### Parameters\n\n- **elements** - Array of tuples where each tuple contains a key and value\n\n### Description\n\nThe `map()` function creates a map from an array of key-value pairs represented\nas tuples. Each tuple in the array must contain exactly two elements: the first\nelement becomes the key, and the second element becomes the value. This format\nis useful when you have structured key-value data.\n\n## `map()`\n\nCreates an empty map.\n\n### Parameters\n\nThis function takes no parameters.\n\n### Description\n\nThe `map()` function creates an empty map with unknown key and value types.\nThis is useful for initializing map variables or as a starting point for\nmap operations. The specific key and value types are inferred from subsequent\nusage context.\n\n## `map(key: value, ...)`\n\nCreates a map from literal key-value pairs.\n\n### Parameters\n\n- **key: value** - Variable number of key-value pairs using colon syntax\n\n### Description\n\nThe `map()` function creates a map from explicitly specified key-value pairs\nusing Hamelin's colon syntax. Each key must be unique within the map. All keys\nmust be of the same type, and all values must be of the same type. This provides\na concise way to create maps with known literal values.\n\n## `map_keys(map)`\n\nExtracts all keys from a map as an array.\n\n### Parameters\n\n- **map** - Map expression\n\n### Description\n\nThe `map_keys()` function returns an array containing all keys from the input\nmap. The order of keys in the resulting array is not guaranteed. If the map\nis empty, it returns an empty array. If the map is null, the function returns null.\n\n## `map_values(map)`\n\nExtracts all values from a map as an array.\n\n### Parameters\n\n- **map** - Map expression\n\n### Description\n\nThe `map_values()` function returns an array containing all values from the input\nmap. The order of values in the resulting array corresponds to the order of keys\nreturned by `map_keys()`, though this order is not guaranteed across calls. If the\nmap is empty, it returns an empty array. If the map is null, the function returns\nnull. This is useful for extracting and processing all values from a map structure.\n\n## `parse_json(json)`\n\nParses a JSON string into a variant type.\n\n### Parameters\n\n- **json** - String expression containing valid JSON\n\n### Description\n\nThe `parse_json()` function parses a JSON string and returns the result as\na variant type that can represent any JSON structure including objects, arrays,\nstrings, numbers, booleans, and null values. If the input string is not valid\nJSON, an error is raised. The variant type preserves the original JSON structure\nand allows dynamic access to nested elements.\n\n## `parse_json(variant)`\n\nReturns a variant value unchanged (identity function for variants).\n\n### Parameters\n\n- **variant** - Variant expression\n\n### Description\n\nWhen `parse_json()` is called with a variant input, it simply returns the\nvariant unchanged. This overload allows `parse_json()` to be safely used\non values that might already be variants without causing errors or unnecessary\nconversions.\n\n## `to_json_string(json)`\n\nConverts a variant to its JSON string representation.\n\n### Parameters\n\n- **json** - Variant expression containing JSON data\n\n### Description\n\nThe `to_json_string()` function converts a variant type back into a JSON string.\nThis is the inverse of `parse_json()`, allowing you to serialize structured data\nback to JSON format. The resulting string is properly formatted JSON that can be\nstored, transmitted, or parsed by other systems. Complex nested structures are\npreserved, and the output follows standard JSON formatting rules.\n\n## `len(collection)`\n\nReturns the number of elements in a collection.\n\n### Parameters\n\n- **collection** - Array or map expression\n\n### Description\n\nThe `len()` function returns the number of elements in arrays or maps as an\ninteger. For arrays, it counts all elements including null values. For maps,\nit counts the number of key-value pairs. If the collection is null, the\nfunction returns null. Empty collections return 0.\n\n## `filter_null(array)`\n\nRemoves null elements from an array.\n\n### Parameters\n\n- **array** - Array expression of any element type\n\n### Description\n\nThe `filter_null()` function returns a new array containing only the non-null\nelements from the input array. The order of remaining elements is preserved.\nIf all elements are null, it returns an empty array. If the input array is\nnull, the function returns null. This function is essential for cleaning\ndata before further processing.",
487
487
  "function-reference/match-group-functions.md": "# Match Group Functions\n\nFunctions for accessing events within pattern matching groups that must be used with the `MATCH` command.\n\n## `first(expression)` / `first(expression, offset)`\n\nReturns the value of an expression from the first event in a match group.\n\n### Parameters\n\n- **expression** - Expression to evaluate from the first event\n- **offset** (optional) - Integer specifying which occurrence to access (default: 0)\n\n### Description\n\nThe `first()` function retrieves the value of the specified expression from\nthe first event in the current match group. When used with the offset parameter,\nit returns the value from the first + offset event. This function is commonly\nused to access timestamps, field values, or calculated expressions from the\nbeginning of a matched event sequence.\n\n## `last(expression)` / `last(expression, offset)`\n\nReturns the value of an expression from the last event in a match group.\n\n### Parameters\n\n- **expression** - Expression to evaluate from the last event\n- **offset** (optional) - Integer specifying which occurrence to access (default: 0)\n\n### Description\n\nThe `last()` function retrieves the value of the specified expression from\nthe last event in the current match group. When used with the offset parameter,\nit returns the value from the last - offset event. This function is commonly\nused to measure durations, access final states, or extract values from the\nend of a matched event sequence.\n\n## `prev(expression)`\n\nReturns the value of an expression from the previous event in the sequence.\n\n### Parameters\n\n- **expression** - Expression to evaluate from the previous event\n\n### Description\n\nThe `prev()` function retrieves the value of the specified expression from\nthe event immediately preceding the current event in the match sequence.\nThis function provides access to the previous event's state, enabling\ncomparisons and calculations that depend on sequential relationships\nbetween events.\n\n## `next(expression)`\n\nReturns the value of an expression from the next event in the sequence.\n\n### Parameters\n\n- **expression** - Expression to evaluate from the next event\n\n### Description\n\nThe `next()` function retrieves the value of the specified expression from\nthe event immediately following the current event in the match sequence.\nThis function enables forward-looking analysis and calculations that depend\non subsequent events in the pattern.",
488
488
  "function-reference/mathematical-functions.md": "# Mathematical Functions\n\nScalar functions for mathematical operations and calculations that can be used in any expression context.\n\n## `abs(x)`\n\nReturns the absolute value of a number.\n\n### Parameters\n\n- **x** - Numeric expression\n\n### Description\n\nThe `abs()` function returns the absolute value (magnitude) of the input number,\nremoving any negative sign. For positive numbers and zero, it returns the value\nunchanged. For negative numbers, it returns the positive equivalent.\n\n## `cbrt(x)`\n\nReturns the cube root of a number.\n\n### Parameters\n\n- **x** - Numeric expression\n\n### Description\n\nThe `cbrt()` function calculates the cube root of the input value. The result\nis always returned as a double-precision floating-point number. Unlike square\nroot, cube root is defined for negative numbers.\n\n## `ceil(x)` / `ceiling(x)`\n\nRounds a number up to the nearest integer.\n\n### Parameters\n\n- **x** - Numeric expression\n\n### Description\n\nThe `ceil()` and `ceiling()` functions round the input value up to the nearest\ninteger. For positive numbers, this means rounding away from zero. For negative\nnumbers, this means rounding toward zero. Both function names are equivalent.\n\n## `degrees(x)`\n\nConverts radians to degrees.\n\n### Parameters\n\n- **x** - Numeric expression representing an angle in radians\n\n### Description\n\nThe `degrees()` function converts an angle from radians to degrees. The result\nis always returned as a double-precision floating-point number. The conversion\nuses the formula: degrees = radians \xD7 (180/\u03C0).\n\n## `e()`\n\nReturns Euler's number (mathematical constant e).\n\n### Parameters\n\nThis function takes no parameters.\n\n### Description\n\nThe `e()` function returns the mathematical constant e (approximately 2.71828),\nwhich is the base of natural logarithms. The result is returned as a\ndouble-precision floating-point number.\n\n## `exp(x)`\n\nReturns e raised to the power of x.\n\n### Parameters\n\n- **x** - Numeric expression representing the exponent\n\n### Description\n\nThe `exp()` function calculates e^x, where e is Euler's number. This is the\nexponential function, which is the inverse of the natural logarithm. The result\nis always returned as a double-precision floating-point number.\n\n## `floor(x)`\n\nRounds a number down to the nearest integer.\n\n### Parameters\n\n- **x** - Numeric expression\n\n### Description\n\nThe `floor()` function rounds the input value down to the nearest integer. For\npositive numbers, this means rounding toward zero. For negative numbers, this\nmeans rounding away from zero.\n\n## `ln(x)`\n\nReturns the natural logarithm of a number.\n\n### Parameters\n\n- **x** - Numeric expression (must be positive)\n\n### Description\n\nThe `ln()` function calculates the natural logarithm (base e) of the input\nvalue. The input must be positive; negative values or zero will result in an\nerror. The result is always returned as a double-precision floating-point number.\n\n## `log(b, x)`\n\nReturns the logarithm of x with the specified base.\n\n### Parameters\n\n- **b** - Numeric expression representing the logarithm base\n- **x** - Numeric expression (must be positive)\n\n### Description\n\nThe `log()` function calculates the logarithm of x using the specified base b.\nBoth the base and the value must be positive. The result is always returned as\na double-precision floating-point number.\n\n## `log10(x)`\n\nReturns the base-10 logarithm of a number.\n\n### Parameters\n\n- **x** - Numeric expression (must be positive)\n\n### Description\n\nThe `log10()` function calculates the common logarithm (base 10) of the input\nvalue. The input must be positive; negative values or zero will result in an\nerror. The result is always returned as a double-precision floating-point number.\n\n## `log2(x)`\n\nReturns the base-2 logarithm of a number.\n\n### Parameters\n\n- **x** - Numeric expression (must be positive)\n\n### Description\n\nThe `log2()` function calculates the binary logarithm (base 2) of the input\nvalue. The input must be positive; negative values or zero will result in an\nerror. The result is always returned as a double-precision floating-point number.\n\n## `pi()`\n\nReturns the mathematical constant \u03C0 (pi).\n\n### Parameters\n\nThis function takes no parameters.\n\n### Description\n\nThe `pi()` function returns the mathematical constant \u03C0 (approximately 3.14159),\nwhich represents the ratio of a circle's circumference to its diameter. The\nresult is returned as a double-precision floating-point number.\n\n## `pow(x, p)` / `power(x, p)`\n\nRaises a number to the specified power.\n\n### Parameters\n\n- **x** - Numeric expression representing the base\n- **p** - Numeric expression representing the exponent\n\n### Description\n\nThe `pow()` and `power()` functions calculate x raised to the power of p (x^p).\nBoth function names are equivalent. The result is always returned as a\ndouble-precision floating-point number.\n\n## `radians(x)`\n\nConverts degrees to radians.\n\n### Parameters\n\n- **x** - Numeric expression representing an angle in degrees\n\n### Description\n\nThe `radians()` function converts an angle from degrees to radians. The result\nis always returned as a double-precision floating-point number. The conversion\nuses the formula: radians = degrees \xD7 (\u03C0/180).\n\n## `round(x)` / `round(x, d)`\n\nRounds a number to the nearest integer or specified decimal places.\n\n### Parameters\n\n- **x** - Numeric expression to round\n- **d** (optional) - Integer specifying the number of decimal places\n\n### Description\n\nThe `round()` function rounds the input value to the nearest integer when used\nwith one parameter, or to the specified number of decimal places when used with\ntwo parameters. The rounding follows standard mathematical rules (0.5 rounds up).\n\n## `sign(x)`\n\nReturns the sign of a number.\n\n### Parameters\n\n- **x** - Numeric expression\n\n### Description\n\nThe `sign()` function returns -1 for negative numbers, 0 for zero, and 1 for\npositive numbers. This function helps determine the sign of a value without\nregard to its magnitude.\n\n## `sqrt(x)`\n\nReturns the square root of a number.\n\n### Parameters\n\n- **x** - Numeric expression (must be non-negative)\n\n### Description\n\nThe `sqrt()` function calculates the square root of the input value. The input\nmust be non-negative; negative values will result in an error. The result is\nalways returned as a double-precision floating-point number.\n\n## `truncate(x)`\n\nRemoves the fractional part of a number.\n\n### Parameters\n\n- **x** - Numeric expression\n\n### Description\n\nThe `truncate()` function removes the fractional part of a number, effectively\nrounding toward zero. For positive numbers, this is equivalent to `floor()`.\nFor negative numbers, this is equivalent to `ceil()`.\n\n## `width_bucket(x, bound1, bound2, n)`\n\nReturns the bucket number for a value in a histogram with equal-width buckets.\n\n### Parameters\n\n- **x** - Numeric expression representing the value to bucket\n- **bound1** - Numeric expression representing the lower bound\n- **bound2** - Numeric expression representing the upper bound \n- **n** - Integer expression representing the number of buckets\n\n### Description\n\nThe `width_bucket()` function determines which bucket a value falls into when\ndividing the range between bound1 and bound2 into n equal-width buckets. Values\noutside the bounds return 0 (below bound1) or n+1 (above bound2).\n\n## `width_bucket(x, bins)`\n\nReturns the bucket number for a value using explicitly defined bucket boundaries.\n\n### Parameters\n\n- **x** - Numeric expression representing the value to bucket\n- **bins** - Array of numeric values representing bucket boundaries\n\n### Description\n\nThe `width_bucket()` function determines which bucket a value falls into using\nan array of explicitly defined bucket boundaries. The function returns the\nindex of the bucket where the value belongs, with 0 for values below the\nlowest boundary and array length + 1 for values above the highest boundary.",
489
+ "function-reference/network-functions.md": "# Network functions\n\nFunctions for working with IP addresses, CIDR ranges, and network operations.\n\n## `cidr_contains(cidr, ip)`\n\nTests whether an IP address falls within a CIDR range.\n\n### Parameters\n\n- **cidr** - String expression representing a CIDR range (e.g., \"192.168.1.0/24\" or \"2001:db8::/32\")\n- **ip** - String expression representing an IP address to test\n\n### Returns\n\n- `true` - The IP address is within the CIDR range\n- `false` - The IP address is outside the CIDR range\n- `null` - Invalid CIDR format, invalid IP format, or either parameter is null\n\n### Description\n\nThe `cidr_contains()` function checks whether the specified IP address falls\nwithin the given CIDR range. It supports both IPv4 and IPv6 addresses and CIDR\nnotations. The function returns `null` for invalid inputs rather than raising\nan error, making it safe to use when processing untrusted network data.\n\n### Examples\n\nCheck if specific IP addresses fall within a CIDR range. The function works\nwith both IPv4 and IPv6 addresses, and returns `true` when the IP is within\nthe range, `false` when outside, or `null` for invalid inputs.\n\n```hamelin\ncidr_contains('192.168.1.0/24', '192.168.1.100')\n# Returns: true\n\ncidr_contains('2001:db8::/32', '2001:db8::1')\n# Returns: true\n\ncidr_contains('10.0.0.0/8', '192.168.1.1')\n# Returns: false\n\ncidr_contains('not_a_cidr', '192.168.1.1')\n# Returns: null\n```\n\nFilter security events to show only traffic originating from internal network\nranges. This pattern is common in security analytics when identifying which\nevents came from inside versus outside the network perimeter.\n\n```hamelin\nFROM security_events\n| WHERE cidr_contains('10.0.0.0/8', source_ip)\n| SELECT timestamp, source_ip, action\n```\n\n## `is_ipv4(ip)`\n\nTests whether a string is a valid IPv4 address.\n\n### Parameters\n\n- **ip** - String expression to test\n\n### Returns\n\n- `true` - The string is a valid IPv4 address\n- `false` - The string is a valid IPv6 address\n- `null` - The string is not a valid IP address, empty string, or null\n\n### Description\n\nThe `is_ipv4()` function validates whether the input string represents a valid\nIPv4 address. It returns `true` for valid IPv4 addresses, `false` for valid\nIPv6 addresses, and `null` for invalid or non-IP strings. This three-valued\nlogic distinguishes between \"definitely IPv4\", \"definitely not IPv4 but valid\nIPv6\", and \"not a valid IP at all\".\n\nNote that IPv4-mapped IPv6 addresses (like `::ffff:192.168.1.1`) are\nnormalized to their IPv4 representation and return `true`, since they\nrepresent IPv4 addresses.\n\n### Examples\n\nTest individual IP addresses to determine their type. The function returns\n`true` for IPv4, `false` for IPv6, and `null` for anything that isn't a valid\nIP address.\n\n```hamelin\nis_ipv4('192.168.1.100')\n# Returns: true\n\nis_ipv4('2001:db8::1')\n# Returns: false\n\nis_ipv4('not_an_ip')\n# Returns: null\n```\n\nFilter network logs to show only events with IPv4 source addresses. This is\nuseful when analyzing traffic patterns or building reports that need to\nseparate IPv4 and IPv6 connections.\n\n```hamelin\nFROM network_logs\n| WHERE is_ipv4(source_ip)\n| SELECT timestamp, source_ip, destination_ip\n```\n\nCount how many connections use IPv4 versus IPv6. The AGG command groups by\nthe result of `is_ipv4()`, creating separate counts for true (IPv4), false\n(IPv6), and null (invalid addresses).\n\n```hamelin\nFROM network_logs\n| AGG ipv4_count = count() BY is_ipv4(source_ip)\n```\n\n## `is_ipv6(ip)`\n\nTests whether a string is a valid IPv6 address.\n\n### Parameters\n\n- **ip** - String expression to test\n\n### Returns\n\n- `true` - The string is a valid IPv6 address\n- `false` - The string is a valid IPv4 address\n- `null` - The string is not a valid IP address, empty string, or null\n\n### Description\n\nThe `is_ipv6()` function validates whether the input string represents a valid\nIPv6 address. It returns `true` for valid IPv6 addresses, `false` for valid\nIPv4 addresses, and `null` for invalid or non-IP strings. This three-valued\nlogic distinguishes between \"definitely IPv6\", \"definitely not IPv6 but valid\nIPv4\", and \"not a valid IP at all\".\n\nNote that IPv4-mapped IPv6 addresses (like `::ffff:192.168.1.1`) are\nnormalized to their IPv4 representation and return `false`, since they\nultimately represent IPv4 addresses.\n\n### Examples\n\nTest individual IP addresses to determine their type. The function recognizes\nall IPv6 formats including compressed notation and the loopback address.\n\n```hamelin\nis_ipv6('2001:db8::1')\n# Returns: true\n\nis_ipv6('::1')\n# Returns: true\n\nis_ipv6('192.168.1.100')\n# Returns: false\n\nis_ipv6('not_an_ip')\n# Returns: null\n```\n\nFilter network logs to show only events with IPv6 source addresses. This lets\nyou analyze IPv6-specific traffic or build reports segmented by IP version.\n\n```hamelin\nFROM network_logs\n| WHERE is_ipv6(source_ip)\n| SELECT timestamp, source_ip, destination_ip\n```\n\nClassify IP addresses by version using a `case` expression. The LET command\ncreates a new field that labels each address as IPv4, IPv6, or Invalid based\non validation results, then aggregates the counts.\n\n```hamelin\nFROM network_logs\n| LET ip_version = case(\n is_ipv4(source_ip): \"IPv4\",\n is_ipv6(source_ip): \"IPv6\",\n \"Invalid\"\n )\n| AGG count() BY ip_version\n```\n\n## Working with network data\n\nDetect external connections originating from internal hosts by combining\nmultiple CIDR checks. The first WHERE filters for internal source addresses,\nwhile the second excludes internal destinations, leaving only outbound\nconnections to external networks.\n\n```hamelin\nFROM network_connections\n| WHERE cidr_contains('10.0.0.0/8', source_ip)\n| WHERE NOT cidr_contains('10.0.0.0/8', destination_ip)\n| SELECT\n timestamp,\n source_ip,\n destination_ip,\n is_ipv4(destination_ip) AS dest_is_ipv4\n```\n\nAnalyze DNS query patterns by IP version. The `case` expression classifies\nresolved IP addresses as A records (IPv4) or AAAA records (IPv6), then counts\nqueries by type to show adoption trends.\n\n```hamelin\nFROM dns_queries\n| LET query_type = case(\n is_ipv4(resolved_ip): \"A\",\n is_ipv6(resolved_ip): \"AAAA\",\n \"OTHER\"\n )\n| AGG query_count = count() BY query_type\n| SORT query_count DESC\n```\n",
489
490
  "function-reference/regular-expression-functions.md": "# Regular Expression Functions\n\nScalar functions for pattern matching and advanced text processing using regular expressions.\n\n## `regexp_count(string, pattern)`\n\nCounts the number of times a regular expression pattern matches in a string.\n\n### Parameters\n\n- **string** - String expression to search within\n- **pattern** - String expression representing the regular expression pattern\n\n### Description\n\nThe `regexp_count()` function returns the number of non-overlapping matches of\nthe specified regular expression pattern within the input string. If no matches\nare found, it returns 0. The pattern uses standard regular expression syntax.\n\n## `regexp_extract_all(string, pattern)` / `regexp_extract_all(string, pattern, group)`\n\nExtracts all matches of a regular expression pattern from a string.\n\n### Parameters\n\n- **string** - String expression to search within\n- **pattern** - String expression representing the regular expression pattern\n- **group** (optional) - Integer specifying which capture group to extract\n\n### Description\n\nThe `regexp_extract_all()` function returns an array containing all matches of\nthe specified pattern. When used with two parameters, it returns the entire\nmatch. When used with three parameters, it returns the specified capture group\nfrom each match.\n\nIf no matches are found, it returns an empty array. Capture groups are numbered\nstarting from 1, with 0 representing the entire match.\n\n## `regexp_extract(string, pattern)` / `regexp_extract(string, pattern, group)`\n\nExtracts the first match of a regular expression pattern from a string.\n\n### Parameters\n\n- **string** - String expression to search within\n- **pattern** - String expression representing the regular expression pattern\n- **group** (optional) - Integer specifying which capture group to extract\n\n### Description\n\nThe `regexp_extract()` function returns the first match of the specified pattern.\nWhen used with two parameters, it returns the entire match. When used with three\nparameters, it returns the specified capture group from the first match.\n\nIf no match is found, it returns null. Capture groups are numbered starting\nfrom 1, with 0 representing the entire match.\n\n## `regexp_like(string, pattern)`\n\nTests whether a string matches a regular expression pattern.\n\n### Parameters\n\n- **string** - String expression to test\n- **pattern** - String expression representing the regular expression pattern\n\n### Description\n\nThe `regexp_like()` function returns true if the input string contains a match\nfor the specified regular expression pattern, false otherwise. This function\ntests for the presence of a match anywhere within the string, not just at the\nbeginning or end.\n\n## `regexp_position(string, pattern)` / `regexp_position(string, pattern, start)` / `regexp_position(string, pattern, start, occurrence)`\n\nReturns the position of a regular expression match within a string.\n\n### Parameters\n\n- **string** - String expression to search within\n- **pattern** - String expression representing the regular expression pattern\n- **start** (optional) - Integer specifying the starting position for the search\n- **occurrence** (optional) - Integer specifying which occurrence to find\n\n### Description\n\nThe `regexp_position()` function returns the 1-based position of the first\ncharacter of a pattern match within the string. When used with the `start`\nparameter, it begins searching from that position. When used with the\n`occurrence` parameter, it finds the nth occurrence of the pattern.\n\nIf no match is found, it returns 0. The start position is 1-based, and the\noccurrence count begins at 1 for the first match.\n\n## `regexp_replace(string, pattern)` / `regexp_replace(string, pattern, replacement)`\n\nReplaces matches of a regular expression pattern in a string.\n\n### Parameters\n\n- **string** - String expression to search within\n- **pattern** - String expression representing the regular expression pattern\n- **replacement** (optional) - String expression to replace matches with\n\n### Description\n\nThe `regexp_replace()` function replaces all matches of the specified pattern.\nWhen used with two parameters, it removes all matches (replaces with empty\nstring). When used with three parameters, it replaces matches with the\nspecified replacement string.\n\nThe replacement string can include capture group references using standard\nregular expression syntax. If no matches are found, the original string is\nreturned unchanged.\n\n## `regexp_split(string, pattern)`\n\nSplits a string using a regular expression pattern as the delimiter.\n\n### Parameters\n\n- **string** - String expression to split\n- **pattern** - String expression representing the regular expression pattern to use as delimiter\n\n### Description\n\nThe `regexp_split()` function splits the input string at each occurrence of\nthe specified pattern and returns an array of the resulting substrings. The\npattern matches are not included in the result array.\n\nIf the pattern is not found, the function returns an array containing the\noriginal string as a single element. If the pattern matches at the beginning\nor end of the string, empty strings may be included in the result array.",
490
491
  "function-reference/string-functions.md": "# String Functions\n\nScalar functions for string processing and manipulation that can be used in any expression context.\n\n## `replace(string, pattern)`\n\nReplaces all occurrences of a pattern in a string.\n\n### Parameters\n\n- **string** - String expression to search within\n- **pattern** - String expression representing the text to replace\n\n### Description\n\nThe `replace()` function removes all occurrences of the specified pattern from\nthe input string. This function performs literal string replacement, not\npattern matching. If the pattern is not found, the original string is returned\nunchanged.\n\n## `starts_with(string, prefix)`\n\nTests whether a string starts with a specified prefix.\n\n### Parameters\n\n- **string** - String expression to test\n- **prefix** - String expression representing the prefix to check for\n\n### Description\n\nThe `starts_with()` function returns true if the input string begins with the\nspecified prefix, false otherwise. The comparison is case-sensitive. An empty\nprefix will always return true for any string.\n\n## `ends_with(string, suffix)`\n\nTests whether a string ends with a specified suffix.\n\n### Parameters\n\n- **string** - String expression to test\n- **suffix** - String expression representing the suffix to check for\n\n### Description\n\nThe `ends_with()` function returns true if the input string ends with the\nspecified suffix, false otherwise. The comparison is case-sensitive. An empty\nsuffix will always return true for any string.\n\n## `contains(string, substring)`\n\nTests whether a string contains a specified substring.\n\n### Parameters\n\n- **string** - String expression to search within\n- **substring** - String expression representing the text to search for\n\n### Description\n\nThe `contains()` function returns true if the input string contains the\nspecified substring anywhere within it, false otherwise. The comparison is\ncase-sensitive. An empty substring will always return true for any string.\n\n## `lower(string)`\n\nConverts a string to lowercase.\n\n### Parameters\n\n- **string** - String expression to convert\n\n### Description\n\nThe `lower()` function converts all uppercase characters in the input string\nto their lowercase equivalents. Characters that are already lowercase or\nnon-alphabetic characters remain unchanged.\n\n## `upper(string)`\n\nConverts a string to uppercase.\n\n### Parameters\n\n- **string** - String expression to convert\n\n### Description\n\nThe `upper()` function converts all lowercase characters in the input string\nto their uppercase equivalents. Characters that are already uppercase or\nnon-alphabetic characters remain unchanged.\n\n## `len(string)`\n\nReturns the length of a string in characters.\n\n### Parameters\n\n- **string** - String expression to measure\n\n### Description\n\nThe `len()` function returns the number of characters in the input string.\nThis counts Unicode characters, not bytes, so multi-byte characters are\ncounted as single characters. An empty string returns 0.",
491
492
  "function-reference/time-date-functions.md": '# Time & Date Functions\n\nScalar functions for temporal data processing and manipulation that can be used in any expression context.\n\n## `now()`\n\nReturns the current timestamp.\n\n### Parameters\n\nThis function takes no parameters.\n\n### Description\n\nThe `now()` function returns the current date and time as a timestamp. The\nexact timestamp represents the moment when the function is evaluated during\nquery execution. All calls to `now()` within the same query execution return\nthe same timestamp value.\n\n## `today()`\n\nReturns today\'s date at midnight.\n\n### Parameters\n\nThis function takes no parameters.\n\n### Description\n\nThe `today()` function returns the current date with the time portion set to\nmidnight (00:00:00). This is equivalent to truncating `now()` to the day\nboundary. The result represents the start of the current day.\n\n## `yesterday()`\n\nReturns yesterday\'s date at midnight.\n\n### Parameters\n\nThis function takes no parameters.\n\n### Description\n\nThe `yesterday()` function returns yesterday\'s date with the time portion set\nto midnight (00:00:00). This is equivalent to subtracting one day from `today()`.\nThe result represents the start of the previous day.\n\n## `tomorrow()`\n\nReturns tomorrow\'s date at midnight.\n\n### Parameters\n\nThis function takes no parameters.\n\n### Description\n\nThe `tomorrow()` function returns tomorrow\'s date with the time portion set to\nmidnight (00:00:00). This is equivalent to adding one day to `today()`. The\nresult represents the start of the next day.\n\n## `ts(timestamp)`\n\nConverts a string to a timestamp.\n\n### Parameters\n\n- **timestamp** - String expression representing a timestamp\n\n### Description\n\nThe `ts()` function parses a string representation of a timestamp and converts\nit to a timestamp type. The function accepts various timestamp formats including\nISO 8601 format. If the string cannot be parsed as a valid timestamp, an error\nis raised.\n\n## `year(timestamp)`\n\nExtracts the year from a timestamp.\n\n### Parameters\n\n- **timestamp** - Timestamp expression\n\n### Description\n\nThe `year()` function extracts the year component from a timestamp and returns\nit as an integer. For example, a timestamp of "2023-07-15 14:30:00" would\nreturn 2023.\n\n## `month(timestamp)`\n\nExtracts the month from a timestamp.\n\n### Parameters\n\n- **timestamp** - Timestamp expression\n\n### Description\n\nThe `month()` function extracts the month component from a timestamp and returns\nit as an integer from 1 to 12, where 1 represents January and 12 represents\nDecember. For example, a timestamp of "2023-07-15 14:30:00" would return 7.\n\n## `day(timestamp)`\n\nExtracts the day of the month from a timestamp.\n\n### Parameters\n\n- **timestamp** - Timestamp expression\n\n### Description\n\nThe `day()` function extracts the day component from a timestamp and returns\nit as an integer from 1 to 31, depending on the month. For example, a timestamp\nof "2023-07-15 14:30:00" would return 15.\n\n## `day_of_week(timestamp)`\n\nExtracts the day of the week from a timestamp.\n\n### Parameters\n\n- **timestamp** - Timestamp expression\n\n### Description\n\nThe `day_of_week()` function extracts the ISO day of the week from a timestamp \nand returns it as an integer from 1 (Monday) to 7 (Sunday).\n\n## `hour(timestamp)`\n\nExtracts the hour from a timestamp.\n\n### Parameters\n\n- **timestamp** - Timestamp expression\n\n### Description\n\nThe `hour()` function extracts the hour component from a timestamp and returns\nit as an integer from 0 to 23, using 24-hour format. For example, a timestamp\nof "2023-07-15 14:30:00" would return 14.\n\n## `minute(timestamp)`\n\nExtracts the minute from a timestamp.\n\n### Parameters\n\n- **timestamp** - Timestamp expression\n\n### Description\n\nThe `minute()` function extracts the minute component from a timestamp and\nreturns it as an integer from 0 to 59. For example, a timestamp of\n"2023-07-15 14:30:00" would return 30.\n\n## `second(timestamp)`\n\nExtracts the second from a timestamp.\n\n### Parameters\n\n- **timestamp** - Timestamp expression\n\n### Description\n\nThe `second()` function extracts the second component from a timestamp and\nreturns it as an integer from 0 to 59. For example, a timestamp of\n"2023-07-15 14:30:45" would return 45.\n\n## `at_timezone(timestamp, timezone)`\n\nConverts a timestamp to a different timezone.\n\n### Parameters\n\n- **timestamp** - Timestamp expression to convert\n- **timezone** - String expression representing the target timezone\n\n### Description\n\nThe `at_timezone()` function converts a timestamp from its current timezone\nto the specified target timezone. The timezone parameter should be a valid\ntimezone identifier such as "UTC", "America/New_York", or "Europe/London".\nThe function returns a new timestamp representing the same moment in time\nbut expressed in the target timezone.\n\n## `to_millis(interval)`\n\nConverts an interval to milliseconds.\n\n### Parameters\n\n- **interval** - Interval expression to convert\n\n### Description\n\nThe `to_millis()` function converts an interval (duration) to its equivalent\nvalue in milliseconds as an integer. This is useful for calculations that\nrequire numeric representations of time durations. For example, an interval\nof "5 minutes" would return 300000 milliseconds.\n\n## `to_nanos(interval)`\n\nConverts an interval to nanoseconds.\n\n### Parameters\n\n- **interval** - Interval expression to convert\n\n### Description\n\nThe `to_nanos()` function converts an interval (duration) to its equivalent\nvalue in nanoseconds as an integer. This provides the highest precision for\ntime duration calculations. The function multiplies the millisecond value\nby 1,000,000 to get nanoseconds. For example, an interval of "1 second"\nwould return 1,000,000,000 nanoseconds.\n\n## `from_millis(millis)`\n\nCreates an interval from milliseconds.\n\n### Parameters\n\n- **millis** - Integer expression representing milliseconds\n\n### Description\n\nThe `from_millis()` function creates an interval from a millisecond value.\nThis is the inverse of `to_millis()`, allowing you to convert numeric\nmillisecond values back into interval types that can be used with timestamp\narithmetic. For example, `from_millis(5000)` creates an interval of 5 seconds.\n\n## `from_nanos(nanos)`\n\nCreates an interval from nanoseconds.\n\n### Parameters\n\n- **nanos** - Integer expression representing nanoseconds\n\n### Description\n\nThe `from_nanos()` function creates an interval from a nanosecond value.\nThis is the inverse of `to_nanos()`, converting numeric nanosecond values\ninto interval types. The function divides the nanosecond value by 1,000,000,000\nto convert to seconds. For example, `from_nanos(1500000000)` creates an\ninterval of 1.5 seconds.\n\n## `from_unixtime_seconds(seconds)`\n\nCreates a timestamp from Unix seconds.\n\n### Parameters\n\n- **seconds** - Integer expression representing seconds since Unix epoch\n\n### Description\n\nThe `from_unixtime_seconds()` function converts a Unix timestamp (seconds\nsince January 1, 1970 UTC) into a timestamp type. This is commonly used\nwhen working with systems that store time as Unix timestamps. For example,\n`from_unixtime_seconds(1625097600)` returns the timestamp "2021-07-01 00:00:00".\n\n## `from_unixtime_millis(millis)`\n\nCreates a timestamp from Unix milliseconds.\n\n### Parameters\n\n- **millis** - Integer expression representing milliseconds since Unix epoch\n\n### Description\n\nThe `from_unixtime_millis()` function converts Unix time in milliseconds\nto a timestamp. Many systems and APIs return timestamps as milliseconds\nsince the Unix epoch. This function handles the conversion by multiplying\nthe input by 1,000,000 to convert to nanoseconds internally. For example,\n`from_unixtime_millis(1625097600000)` returns "2021-07-01 00:00:00".\n\n## `from_unixtime_micros(micros)`\n\nCreates a timestamp from Unix microseconds.\n\n### Parameters\n\n- **micros** - Integer expression representing microseconds since Unix epoch\n\n### Description\n\nThe `from_unixtime_micros()` function converts Unix time in microseconds\nto a timestamp. This provides microsecond precision for systems that require\nit. The function multiplies the input by 1,000 to convert to nanoseconds\ninternally. For example, `from_unixtime_micros(1625097600000000)` returns\n"2021-07-01 00:00:00".\n\n## `from_unixtime_nanos(nanos)`\n\nCreates a timestamp from Unix nanoseconds.\n\n### Parameters\n\n- **nanos** - Integer expression representing nanoseconds since Unix epoch\n\n### Description\n\nThe `from_unixtime_nanos()` function converts Unix time in nanoseconds\ndirectly to a timestamp. This provides the highest precision for timestamp\nconversion and is useful when working with high-frequency data or systems\nthat track time at nanosecond granularity. For example,\n`from_unixtime_nanos(1625097600000000000)` returns "2021-07-01 00:00:00".\n\n## `to_unixtime(timestamp)`\n\nConverts a timestamp to Unix seconds.\n\n### Parameters\n\n- **timestamp** - Timestamp expression to convert\n\n### Description\n\nThe `to_unixtime()` function converts a timestamp to Unix time, returning\nthe number of seconds since January 1, 1970 UTC as a double-precision\nfloating-point number. The fractional part represents sub-second precision.\nThis is useful for interoperability with systems that expect Unix timestamps.\nFor example, the timestamp "2021-07-01 00:00:00" returns 1625097600.0.',
@@ -508,7 +509,7 @@ scoring weights without affecting the overall detection logic.`,
508
509
  "types/casting.md": "# Casting\n\nTo cast, use the infix operator `AS`. Hamelin uses the `AS` operator for explicit type casting. You write the value, then `AS`, then the type you want.\n\nThe two most common reasons to cast are:\n\n- Casting variant to explicit types after parsing JSON\n- Casting types to string to concatenate them together\n\n## Basic syntax\n\nCast a value by putting `AS` between the value and the target type:\n\n```hamelin\n| LET x = 5 AS double\n```\n\nThis creates a double-precision value instead of an integer.\n\n## Why `AS` for casting?\n\nYou'll use explicit casting often, especially when declaring literals to influence type inference. We wanted something terse. Using `AS` for assignment confuses people (the order seems backwards). This frees up `AS` for casting, which reads cleanly: *treat this one thing as another type*.\n\n## How it works\n\nThe `AS` operator translates explicit cast expressions into the generated code. We often actually translate to `try_cast()` in order to make sure the query doesn't crash.\n\nHamelin delegates **implicit casting to the underlying engine** \u2014 if you assign a value to a typed column or pass it to a function that expects a different type, the engine decides whether and how to cast the value.\n\n## Common casting examples\n\n### String conversions\nConvert values to strings for display or storage:\n\n```hamelin\nFROM events\n| SELECT\n user_id_str = user_id AS string,\n timestamp_str = timestamp AS string,\n status_display = status_code AS string\n```\n\n### Numeric conversions\nConvert between different numeric types or from strings to numbers:\n\n```hamelin\nFROM logs\n| SELECT\n status_code = response_code AS integer,\n response_time = response_time_str AS double,\n user_count = total_users AS integer\n```\n\n### Boolean conversions\nConvert various values to boolean types:\n\n```hamelin\nFROM user_data\n| SELECT\n user_id,\n is_active = status_flag AS boolean,\n has_permissions = permission_level AS boolean\n```\n\n## Type inference with casting\n\nYou can influence type inference in variable declarations by casting literals:\n\n```hamelin\nFROM events\n| LET\n threshold = 100 AS double,\n max_retries = 5 AS integer,\n default_timeout = 30.0 AS double\n| WHERE response_time > threshold\n```\n\n## Complex type casting\n\n### Array casting\nCast arrays to specific element types:\n\n```hamelin\nFROM json_data\n| SELECT\n tags = tag_list AS array(string),\n scores = score_array AS array(double)\n```\n\n### Struct casting\nCast structured data to specific field types:\n\n```hamelin\nFROM structured_data\n| SELECT\n user_info = user_data AS {name: string, email: string},\n coordinates = location AS {x: double, y: double}\n```\n",
509
510
  "types/map.md": "# Map\n\nYou use maps to store key-value pairs where you have too many different keys to create separate columns. Hamelin's **map** type matches SQL's design - it's a homogeneous structure where all keys have the same type and all values have the same type.\n\n## When to use maps\n\nYou should use maps rarely. Before using a map, consider whether you could:\n- Factor subset key spaces into separate tables\n- Leave the data as a JSON string and parse only commonly needed values into columns\n\nYou use maps only when you have a high cardinality key space that can't be handled with these alternatives.\n\n## Creating maps\n\nYou construct maps using the `map()` function, which has two overloaded forms.\n\n### Map literals\n\nYou create a map by listing key-value pairs directly:\n\n```hamelin\nLET config = map(\n 'timeout': 30,\n 'retries': 3,\n 'debug': false\n)\n```\n\n### From key and value arrays\n\nYou build a map from separate arrays of keys and values:\n\n```hamelin\nLET field_names = ['user_id', 'email', 'created_at']\nLET field_values = [12345, 'user@example.com', '2024-01-15']\nLET user_data = map(field_names, field_values)\n```\n\n\n\n## Empty maps\n\nYou create an empty map using the function without arguments:\n\n```hamelin\nLET empty_config = map()\n```\n\n## Type homogeneity\n\nMaps must be homogeneous - all values must have the same type. This example will generate an error:\n\n```hamelin\n// ERROR: mixing integer and string values\nLET broken_map = map(\n 'count': 42,\n 'name': 'example'\n)\n```\n\n## Accessing map values\n\nYou use bracket notation to retrieve values by key:\n\n```hamelin\nFROM events\n| LET metadata = map('source': 'api', 'version': 2)\n| SELECT event_source = metadata['source']\n```\n\n## Map storage\n\nThe underlying engine stores each map as a pair of related columns - one for keys and one for values. Row positions in each column relate the keys to their values.\n\n## Performance considerations\n\nUnlike structs, which add overhead by creating a column per field, maps have minimal impact on table width (only two columns). However, you pay the cost of key values being actual data rather than column names, even though they're dictionary encoded.\n",
510
511
  "types/philosophy.md": "# Type Philosophy\n\nHamelin is a typed language, which prevents query mistakes, provides better\nerror messages, and simplifies translation definitions.\n\n## Design Philosophy\n\nYou mostly won't think about types when writing queries. You'll learn about\nthem when something doesn't type check and Hamelin gives you a clear error\nmessage. Hamelin catches type errors before they reach the SQL engine, so you\nget helpful feedback about your Hamelin code instead of confusing messages about\ngenerated SQL.\n\n### Umbrella Types\n\nHamelin groups related types under umbrella categories instead of exposing every\nSQL type variation. As an example, all integer types from `tinyint` to `bigint` become the\nsingle `integer` type for type checking. You can reason about your code more\neasily while still getting precise error messages.\n\n### Error Prevention\n\nTypes catch mistakes early and give you clear feedback. When something doesn't\ntype check, errors point to your Hamelin code, not to generated SQL that would\nconfuse you.\n\n### Transparent Mapping\n\nHamelin types map cleanly to SQL types without requiring you to think about\nstorage details. The system handles these details automatically while preserving\nthe semantic meaning of your data.\n\n### Function Overloading\n\nHamelin's type system allows it to use the same function name for operations on\ndifferent types. You write `sum()` whether you're aggregating a column or adding\nup an array - no need for separate `sum_numbers()` and `array_sum()` functions.\nThis makes it easier on the author. It also makes it possible for Hamelin's\nimplementation to define dialect-specific translations.\n\n## Type Inference\n\nHamelin figures out types from your expressions and data automatically. You\ndon't need to declare types. Hamelin determines them based on how you use\nvalues in operations and functions.\n\nFor example:\n- `42 + 3.14e0` results in a `double` (floating-point arithmetic)\n- `'hello' + 'world'` results in a `string` (string concatenation)\n- `timestamp > '2024-01-01'` results in a `boolean` (comparison operation)\n- `sum(revenue)` works as an aggregation function in `AGG` commands\n- `sum([1, 2, 3])` works on arrays and returns `6`\n\nType inference also powers Hamelin's function translation, ensuring that\noperations translate to the right SQL functions based on the inferred types of\ntheir arguments. This lets you focus on expressing your logic clearly while the\ntype system works behind the scenes to ensure correctness and give you helpful\nfeedback when things go wrong.\n",
511
- "types/primitive-types.md": "# Primitive Types\n\nHamelin provides several primitive types that serve as building blocks for more complex data structures. These types map cleanly to SQL types while providing a simplified interface for type checking and operations.\n\n## Boolean\n\nThe `boolean` type translates directly to SQL's `boolean` type.\n\nExamples: `true`, `false`\n\n## Int\n\nThe `int` type is an umbrella for all integer types, from `tinyint` (8 bits)\nto `bigint` (64 bits). All integers are treated the same for type checking\npurposes.\n\nExamples: `1`, `42`, `-17`, `1000000`\n\n## Double (Floating Point)\n\nThe `double` type represents floating point numbers with variable precision. Use\ndoubles for calculations where approximate values are acceptable. Double\nliterals can be written in scientific notation (e.g., `1.5e0`). The `e0` means\n\"times 10 to the power of 0\", so `1.5e0` equals `1.5` but forces the result to\nbe a double type.\n\nExamples: `3.14159e0`, `2.5e0`, `1.23e-4`, `-9.87e2`\n\n## Decimal (Fixed Point)\n\nThe `decimal` type represents exact numeric values with specified precision and\nscale, written as `decimal(precision, scale)`. Decimal literals in Hamelin\ndefault to fixed point because most business calculations require exact\narithmetic rather than floating point approximations.\n\nExamples: `100.50`, `0.075`, `999.99`\n\n## String\n\nThe `string` type covers `char`, `varchar` (of any length). String concatenation\nuses the `+` operator.\n\nExamples: `'hello world'`, `\"error message\"`, `'user@example.com'`\n\n## Binary\n\nThe `binary` type translates to `varbinary` in SQL for handling binary data.\n\nExamples: Binary data representations for file contents, encrypted data, or raw bytes.\n\n## Timestamp\n\nThe `timestamp` type is an umbrella for `date`, `timestamp`, and all their\nvariants (precision and timezone). Hamelin follows SQL's assumption that\ntimestamps with and without zones can be compared.\n\nExamples: `ts('2024-01-15')`, `ts('2024-01-15 14:30:00')`, `ts('2024-01-15T14:30:00Z')`\n\n## Interval\n\nThe `interval` type covers `interval day to second` for time duration\ncalculations with fixed durations. These intervals represent exact amounts of\ntime that can be directly compared and calculated.\n\nExamples: `2h`, `30min`, `5d`\n\n## Calendar Interval\n\nThe `calendar interval` type covers `interval year to month` for calendar-based\ndurations. Calendar intervals like years and months don't represent a fixed\nnumber of days because months have different lengths and years can be leap\nyears. These intervals cannot be directly compared to day-based intervals.\n\nExamples: `3mon`, `2y`, `1q`\n\n## Range\n\nThe `range` type represents spans between two values of any type. Ranges\nare created by the `..` operator. Ranges can be bounded (with both start and end\npoints) or unbounded (extending infinitely in one direction). You see them most\ncommonly in Hamelin as `range(timestamp)` or `range(interval)`, and every query\ngenerally operates under the constraints of a `range(timestamp)`.\n\nExamples: `-2hr..-1hr`, `ts('2024-01-15')..now()`, `-1hr..`, `..now()`, `1..10`, `'a'..'z'`\n\n## Rows\n\nThe `rows` type represents a number of rows and is only useful when declaring\nwindow frames as a certain number of rows rather than a time-based frame.\n\nExamples: `5r`, `10r`, `0r`\n",
512
+ "types/primitive-types.md": "# Primitive Types\n\nHamelin provides several primitive types that serve as building blocks for more complex data structures. These types map cleanly to SQL types while providing a simplified interface for type checking and operations.\n\n## Boolean\n\nThe `boolean` type translates directly to SQL's `boolean` type.\n\nExamples: `true`, `false`\n\n## Int\n\nThe `int` type is an umbrella for all integer types, from `tinyint` (8 bits)\nto `bigint` (64 bits). All integers are treated the same for type checking\npurposes.\n\nExamples: `1`, `42`, `-17`, `1000000`\n\n## Double (Floating Point)\n\nThe `double` type represents floating point numbers with variable precision. Use\ndoubles for calculations where approximate values are acceptable. Double\nliterals can be written in scientific notation (e.g., `1.5e0`). The `e0` means\n\"times 10 to the power of 0\", so `1.5e0` equals `1.5` but forces the result to\nbe a double type.\n\nExamples: `3.14159e0`, `2.5e0`, `1.23e-4`, `-9.87e2`\n\n## Decimal (Fixed Point)\n\nThe `decimal` type represents exact numeric values with specified precision and\nscale, written as `decimal(precision, scale)`. Decimal literals in Hamelin\ndefault to fixed point because most business calculations require exact\narithmetic rather than floating point approximations.\n\nExamples: `100.50`, `0.075`, `999.99`\n\n## String\n\nThe `string` type covers `char`, `varchar` (of any length). String concatenation\nuses the `+` operator.\n\nExamples: `'hello world'`, `\"error message\"`, `'user@example.com'`\n\n## Binary\n\nThe `binary` type translates to `varbinary` in SQL for handling binary data.\n\nExamples: Binary data representations for file contents, encrypted data, or raw bytes.\n\n## Timestamp\n\nThe `timestamp` type is an umbrella for `date`, `timestamp`, and all their\nvariants (precision and timezone). Hamelin follows SQL's assumption that\ntimestamps with and without zones can be compared.\n\nExamples: `ts('2024-01-15')`, `ts('2024-01-15 14:30:00')`, `ts('2024-01-15T14:30:00Z')`\n\n## Interval\n\nThe `interval` type covers `interval day to second` for time duration\ncalculations with fixed durations. These intervals represent exact amounts of\ntime that can be directly compared and calculated. You can multiply and divide\nintervals by both integers and floating-point numbers because they represent\nfixed durations.\n\nExamples: `2h`, `30min`, `5d`, `2h * 2.5`, `6h / 2`\n\n## Calendar Interval\n\nThe `calendar interval` type covers `interval year to month` for calendar-based\ndurations. Calendar intervals like years and months don't represent a fixed\nnumber of days because months have different lengths and years can be leap\nyears. These intervals cannot be directly compared to day-based intervals.\n\nCalendar interval arithmetic only supports integer multipliers and divisors.\nYou cannot multiply or divide calendar intervals by floating-point numbers\nbecause fractional months or years don't have clear meaning in calendar\narithmetic.\n\n**Valid examples:** `3mon`, `2y`, `1q`, `6mon * 2`, `2y / 4`\n\n**Invalid examples:**\n- `1y * 2.5` \u2192 Error: Calendar intervals require integer arithmetic\n- `3mon / 1.5` \u2192 Error: Calendar intervals require integer arithmetic\n\n## Range\n\nThe `range` type represents spans between two values of any type. Ranges\nare created by the `..` operator. Ranges can be bounded (with both start and end\npoints) or unbounded (extending infinitely in one direction). You see them most\ncommonly in Hamelin as `range(timestamp)` or `range(interval)`, and every query\ngenerally operates under the constraints of a `range(timestamp)`.\n\nExamples: `-2hr..-1hr`, `ts('2024-01-15')..now()`, `-1hr..`, `..now()`, `1..10`, `'a'..'z'`\n\n## Rows\n\nThe `rows` type represents a number of rows and is only useful when declaring\nwindow frames as a certain number of rows rather than a time-based frame.\n\nExamples: `5r`, `10r`, `0r`\n",
512
513
  "types/struct.md": '# Struct\n\nStructs let you group related fields together into a single data structure. Unlike SQL\'s `ROW` type, Hamelin structs use field names rather than position to identify each field, making them safer and easier to work with.\n\n## Creating structs\n\nYou create structs using curly braces with field names and values.\n\n```hamelin\nLET user_data = {\n user_id: 12345,\n name: "Alice Johnson",\n email: "alice@example.com"\n}\n```\n\nThis creates a struct with three fields: `user_id`, `name`, and `email`. Each field has a name and a value.\n\n## Accessing struct fields\n\nUse dot notation to access individual fields within a struct.\n\n```hamelin\nFROM user_events\n| WHERE user_info.user_id == 12345\n| SELECT\n user_name = user_info.name,\n user_email = user_info.email\n```\n\nYou can access any field by name, regardless of the order they were defined in the struct.\n\n## Field order and naming\n\nStructs maintain the order of fields as you define them, but field identity\nduring type expansion comes from the name, not the position. You can read more\nabout that in [type expansion](../smart-features/type-expansion.md).\n\n```hamelin\n// These two structs declare different types, but they can be aligned during expansion\nLET\n profile1 = {\n id: 1001,\n status: "active",\n created: ts(\'2024-01-15T00:00:00\')\n },\n profile2 = {\n status: "inactive",\n id: 1002,\n created: ts(\'2024-01-16T00:00:00\')\n }\n```\n\n## Nested structs\n\nStructs can contain other structs for organizing complex data.\n\n```hamelin\nFROM events\n| SELECT structured_event = {\n user: {\n id: user_id,\n profile: {\n name: user_name,\n email: email_address\n }\n },\n event: {\n type: event_type,\n timestamp: event_time,\n source: data_source\n }\n }\n```\n\nAccess nested fields by chaining field names with dots:\n\n```hamelin\n| SELECT\n user_name = structured_event.user.profile.name,\n event_time = structured_event.event.timestamp\n```\n\n## Practical examples\n\nStructs work well for organizing related information that belongs together:\n\n```hamelin\n// HTTP request logging\nFROM access_logs\n| SELECT request_data = {\n request: {\n method: http_method,\n path: url_path,\n status: response_code\n },\n timing: {\n start_time: request_start,\n end_time: request_end,\n duration_ms: response_time\n },\n client: {\n ip: client_ip,\n user_agent: user_agent\n }\n }\n```\n\nThis creates clean, organized output where related fields are grouped logically rather than scattered across many columns.\n',
513
514
  "types/variant.md": "# Variant\n\nYou use Hamelin's **variant** type to work with JSON and other semi-structured data. Hamelin adopts the Variant trend for representing the JSON object model, making JSON feel native and easy to work with.\n\n## Parsing JSON into variant\n\nYou parse a JSON string into a variant using the `parse_json()` function:\n\n```hamelin\nFROM api_logs\n| LET event_data = parse_json(json_payload)\n| SELECT event_data\n```\n\n## Navigating variant data\n\nYou navigate variant substructure safely and ergonomically using dots and square brackets, just like with structs and arrays:\n\n```hamelin\nFROM car_sales\n| LET json = parse_json(src)\n| SELECT \n sale_date = json.date,\n salesperson_name = json.salesperson.name,\n customer_name = json.customer[0].name\n```\n\n## Accessing nested fields\n\nYou can access deeply nested fields using the same dot and bracket notation:\n\n```hamelin\nFROM events\n| LET data = parse_json(event_json)\n| SELECT \n user_email = data.user.profile.email,\n first_item_price = data.transaction.items[0].price\n```\n\n## Safe type conversion\n\nVariants cast safely to other Hamelin types. Individual conversion failures become `NULL` instead of crashing your query:\n\n```hamelin\nFROM logs\n| LET parsed = parse_json(log_data)\n| SELECT \n log_time = parsed.timestamp AS timestamp,\n user_id = parsed.user_id AS string,\n event_count = parsed.count AS integer\n```\n\n## Casting to structured types\n\nYou can cast variants to maps, arrays, and structs. These casts are safe and null on failure:\n\n```hamelin\nFROM json_data\n| LET parsed = parse_json(raw_json)\n| SELECT \n user_info = parsed AS {name: string, age: integer},\n tag_list = parsed.tags AS [string]\n```\n\n## Creating variant objects\n\nYou create variant objects by casting structs to variant:\n\n```hamelin\nLET user_struct = {name: 'Alice', age: 30}\n| LET user_variant = user_struct AS variant\n| SELECT user_variant\n```\n\n## Creating variant arrays\n\nYou create variant arrays by casting arrays to variant:\n\n```hamelin\nLET numbers = [1, 2, 3, 4, 5]\n| LET variant_list = numbers AS variant\n| SELECT variant_list\n```\n\n## Mixed type handling\n\nVariant handles mixed types within the same structure:\n\n```hamelin\nFROM api_responses\n| LET response = parse_json(response_body)\n| SELECT \n record_id = response.data.id AS string,\n is_active = response.data.active AS boolean,\n user_score = response.data.score AS double\n```\n\n## Database system compatibility\n\nHamelin adapts to your database system's JSON capabilities:\n\n- **Full VARIANT support** (Snowflake, Databricks): Hamelin uses native variant storage and operations\n- **JSON support** (BigQuery, Postgres): Hamelin treats JSON as the variant format \n- **ANSI JSON only**: Hamelin provides parsing and access functions but no efficient storage\n\n## Working with arrays\n\nYou access array elements using zero-based indexing:\n\n```hamelin\nFROM events\n| LET data = parse_json(event_data)\n| SELECT \n first_item = data.items[0].name,\n last_item = data.items[-1].name\n```\n\n## Handling missing fields\n\nVariant navigation is safe - accessing missing fields returns `NULL`:\n\n```hamelin\nFROM logs\n| LET parsed = parse_json(log_entry)\n| SELECT \n always_present = parsed.required_field,\n might_be_null = parsed.optional_field\n```\n\n---\n\n*Variant types make JSON feel native in Hamelin, providing safe navigation and conversion without requiring upfront schema knowledge.*"
514
515
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamelin.sh/documentation",
3
- "version": "0.2.9-prerelease.20251024T161808",
3
+ "version": "0.2.9",
4
4
  "sideEffects": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",