@perses-dev/tempo-plugin 0.52.0 → 0.53.0

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 (181) hide show
  1. package/__mf/js/Tempo.2f6d49a1.js +5 -0
  2. package/__mf/js/async/1101.0dae4724.js +73 -0
  3. package/__mf/js/async/1576.83b7de56.js +1 -0
  4. package/__mf/js/async/1964.e6c5b93b.js +2 -0
  5. package/__mf/js/async/2292.75e9aa11.js +2 -0
  6. package/__mf/js/async/2913.f73e6635.js +7 -0
  7. package/__mf/js/async/2913.f73e6635.js.LICENSE.txt +21 -0
  8. package/__mf/js/async/2981.8fe4ed12.js +2 -0
  9. package/__mf/js/async/3224.ce173388.js +1 -0
  10. package/__mf/js/async/3863.8d56ecec.js +2 -0
  11. package/__mf/js/async/3960.401ff0b0.js +2 -0
  12. package/__mf/js/async/3980.4d5490b2.js +2 -0
  13. package/__mf/js/async/4075.4c5ac93a.js +1 -0
  14. package/__mf/js/async/4238.7962a7a1.js +1 -0
  15. package/__mf/js/async/4269.0cfaf9bb.js +2 -0
  16. package/__mf/js/async/4368.bd6fd0e7.js +2 -0
  17. package/__mf/js/async/4368.bd6fd0e7.js.LICENSE.txt +15 -0
  18. package/__mf/js/async/4421.14238d27.js +1 -0
  19. package/__mf/js/async/4535.7c92d3fd.js +1 -0
  20. package/__mf/js/async/5214.c44cbfe5.js +1 -0
  21. package/__mf/js/async/5266.1c520126.js +10 -0
  22. package/__mf/js/async/5409.676a5f3f.js +1 -0
  23. package/__mf/js/async/5790.b4d4134d.js +1 -0
  24. package/__mf/js/async/5876.47f40562.js +2 -0
  25. package/__mf/js/async/{7832.9f2a70d0.js.LICENSE.txt → 5876.47f40562.js.LICENSE.txt} +1 -23
  26. package/__mf/js/async/5981.c6edce96.js +2 -0
  27. package/__mf/js/async/6292.463b4f49.js +1 -0
  28. package/__mf/js/async/6333.367d6758.js +2 -0
  29. package/__mf/js/async/6495.eae3d4b4.js +1 -0
  30. package/__mf/js/async/6527.5341d09f.js +2 -0
  31. package/__mf/js/async/6751.03514b88.js +1 -0
  32. package/__mf/js/async/6770.d9c238f2.js +1 -0
  33. package/__mf/js/async/694.c2c37771.js +1 -0
  34. package/__mf/js/async/7127.a0877987.js +38 -0
  35. package/__mf/js/async/7376.588b7a17.js +1 -0
  36. package/__mf/js/async/738.93d35dc9.js +1 -0
  37. package/__mf/js/async/7740.0500bfc6.js +1 -0
  38. package/__mf/js/async/8216.2f92a883.js +1 -0
  39. package/__mf/js/async/8488.6c9a25e4.js +1 -0
  40. package/__mf/js/async/8597.d5ba4ca7.js +1 -0
  41. package/__mf/js/async/8930.dee9777e.js +1 -0
  42. package/__mf/js/async/9173.69dc268d.js +2 -0
  43. package/__mf/js/async/9314.4b8565a0.js +2 -0
  44. package/__mf/js/async/9368.f0418d24.js +101 -0
  45. package/__mf/js/async/__federation_expose_TempoDatasource.fa3e24f9.js +2 -0
  46. package/__mf/js/async/__federation_expose_TempoExplorer.9949b25e.js +2 -0
  47. package/__mf/js/async/__federation_expose_TempoTraceQuery.13537765.js +1 -0
  48. package/__mf/js/async/lib-router.312ca028.js +2 -0
  49. package/__mf/js/main.edbb7a5a.js +5 -0
  50. package/lib/cjs/components/AttributeFilters.js +274 -0
  51. package/lib/cjs/components/TraceQLEditor.js +9 -3
  52. package/lib/cjs/components/complete.js +29 -13
  53. package/lib/cjs/components/filter/filter.js +25 -0
  54. package/lib/cjs/components/filter/filter_to_traceql.js +66 -0
  55. package/lib/cjs/components/filter/index.js +32 -0
  56. package/lib/cjs/components/filter/traceql_to_filter.js +113 -0
  57. package/lib/cjs/components/index.js +1 -0
  58. package/lib/cjs/explore/TempoExplorer.js +25 -12
  59. package/lib/cjs/explore/links.js +77 -0
  60. package/lib/cjs/plugins/tempo-trace-query/TempoTraceQueryEditor.js +94 -30
  61. package/lib/cjs/plugins/tempo-trace-query/get-trace-data.js +8 -0
  62. package/lib/cjs/plugins/tempo-trace-query/query-editor-model.js +3 -44
  63. package/lib/components/AttributeFilters.d.ts +9 -0
  64. package/lib/components/AttributeFilters.d.ts.map +1 -0
  65. package/lib/components/AttributeFilters.js +261 -0
  66. package/lib/components/AttributeFilters.js.map +1 -0
  67. package/lib/components/TraceQLEditor.d.ts +3 -3
  68. package/lib/components/TraceQLEditor.d.ts.map +1 -1
  69. package/lib/components/TraceQLEditor.js +9 -3
  70. package/lib/components/TraceQLEditor.js.map +1 -1
  71. package/lib/components/complete.d.ts +10 -1
  72. package/lib/components/complete.d.ts.map +1 -1
  73. package/lib/components/complete.js +26 -7
  74. package/lib/components/complete.js.map +1 -1
  75. package/lib/components/filter/filter.d.ts +17 -0
  76. package/lib/components/filter/filter.d.ts.map +1 -0
  77. package/lib/components/filter/filter.js +17 -0
  78. package/lib/components/filter/filter.js.map +1 -0
  79. package/lib/components/filter/filter_to_traceql.d.ts +9 -0
  80. package/lib/components/filter/filter_to_traceql.d.ts.map +1 -0
  81. package/lib/components/filter/filter_to_traceql.js +63 -0
  82. package/lib/components/filter/filter_to_traceql.js.map +1 -0
  83. package/lib/components/filter/index.d.ts +4 -0
  84. package/lib/components/filter/index.d.ts.map +1 -0
  85. package/lib/components/filter/index.js +17 -0
  86. package/lib/components/filter/index.js.map +1 -0
  87. package/lib/components/filter/traceql_to_filter.d.ts +9 -0
  88. package/lib/components/filter/traceql_to_filter.d.ts.map +1 -0
  89. package/lib/components/filter/traceql_to_filter.js +110 -0
  90. package/lib/components/filter/traceql_to_filter.js.map +1 -0
  91. package/lib/components/index.d.ts +1 -0
  92. package/lib/components/index.d.ts.map +1 -1
  93. package/lib/components/index.js +1 -0
  94. package/lib/components/index.js.map +1 -1
  95. package/lib/explore/TempoExplorer.d.ts.map +1 -1
  96. package/lib/explore/TempoExplorer.js +25 -12
  97. package/lib/explore/TempoExplorer.js.map +1 -1
  98. package/lib/explore/links.d.ts +3 -0
  99. package/lib/explore/links.d.ts.map +1 -0
  100. package/lib/explore/links.js +63 -0
  101. package/lib/explore/links.js.map +1 -0
  102. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.d.ts +6 -0
  103. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.d.ts.map +1 -1
  104. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.js +87 -31
  105. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.js.map +1 -1
  106. package/lib/plugins/tempo-trace-query/get-trace-data.js +8 -0
  107. package/lib/plugins/tempo-trace-query/get-trace-data.js.map +1 -1
  108. package/lib/plugins/tempo-trace-query/query-editor-model.d.ts +0 -9
  109. package/lib/plugins/tempo-trace-query/query-editor-model.d.ts.map +1 -1
  110. package/lib/plugins/tempo-trace-query/query-editor-model.js +0 -35
  111. package/lib/plugins/tempo-trace-query/query-editor-model.js.map +1 -1
  112. package/mf-manifest.json +104 -265
  113. package/mf-stats.json +112 -270
  114. package/package.json +6 -6
  115. package/__mf/js/1096.c549c391.js +0 -5
  116. package/__mf/js/Tempo.e7d268a6.js +0 -5
  117. package/__mf/js/async/1465.21c847e0.js +0 -1
  118. package/__mf/js/async/1540.089c4f28.js +0 -74
  119. package/__mf/js/async/1620.45989def.js +0 -2
  120. package/__mf/js/async/1964.75933dd4.js +0 -2
  121. package/__mf/js/async/2114.28503adb.js +0 -1
  122. package/__mf/js/async/2823.df67fd4b.js +0 -2
  123. package/__mf/js/async/3044.8b419ccf.js +0 -1
  124. package/__mf/js/async/3090.90251187.js +0 -2
  125. package/__mf/js/async/3224.8d499a63.js +0 -1
  126. package/__mf/js/async/3355.8bd6f6bd.js +0 -1
  127. package/__mf/js/async/3828.d981b319.js +0 -2
  128. package/__mf/js/async/3960.2228bf7e.js +0 -2
  129. package/__mf/js/async/3980.c94e78cd.js +0 -2
  130. package/__mf/js/async/4075.4c40db9f.js +0 -1
  131. package/__mf/js/async/4238.db631f1f.js +0 -1
  132. package/__mf/js/async/4289.5e2073e0.js +0 -1
  133. package/__mf/js/async/4421.07335985.js +0 -1
  134. package/__mf/js/async/4758.cb86850e.js +0 -1
  135. package/__mf/js/async/5207.e63b049c.js +0 -2
  136. package/__mf/js/async/5214.fb1215df.js +0 -1
  137. package/__mf/js/async/5220.80e3b05e.js +0 -10
  138. package/__mf/js/async/528.2759052c.js +0 -1
  139. package/__mf/js/async/5503.6e47fa95.js +0 -1
  140. package/__mf/js/async/5790.949d8d1c.js +0 -1
  141. package/__mf/js/async/5913.d10c6185.js +0 -73
  142. package/__mf/js/async/5924.bfb4b2fd.js +0 -2
  143. package/__mf/js/async/5981.4700ddf6.js +0 -2
  144. package/__mf/js/async/6292.2481b399.js +0 -1
  145. package/__mf/js/async/6770.4b9911ea.js +0 -1
  146. package/__mf/js/async/694.91676c53.js +0 -1
  147. package/__mf/js/async/7127.ccd78bd8.js +0 -38
  148. package/__mf/js/async/7376.a69c2e5a.js +0 -1
  149. package/__mf/js/async/738.2cdddba7.js +0 -1
  150. package/__mf/js/async/7740.1ecb3732.js +0 -1
  151. package/__mf/js/async/7832.9f2a70d0.js +0 -7
  152. package/__mf/js/async/8485.434a672e.js +0 -28
  153. package/__mf/js/async/8488.d3005164.js +0 -1
  154. package/__mf/js/async/8597.07c3a890.js +0 -1
  155. package/__mf/js/async/8930.ae855fbe.js +0 -1
  156. package/__mf/js/async/9173.83562213.js +0 -2
  157. package/__mf/js/async/9478.57f45cd9.js +0 -2
  158. package/__mf/js/async/__federation_expose_TempoDatasource.4f96e206.js +0 -2
  159. package/__mf/js/async/__federation_expose_TempoExplorer.eb09c758.js +0 -2
  160. package/__mf/js/async/__federation_expose_TempoTraceQuery.4183e294.js +0 -1
  161. package/__mf/js/async/lib-router.46460b13.js +0 -2
  162. package/__mf/js/main.4498dd88.js +0 -1
  163. /package/__mf/css/async/{4758.c10cf504.css → 1576.c10cf504.css} +0 -0
  164. /package/__mf/css/async/{5207.c10cf504.css → 9314.c10cf504.css} +0 -0
  165. /package/__mf/js/async/{5913.d10c6185.js.LICENSE.txt → 1101.0dae4724.js.LICENSE.txt} +0 -0
  166. /package/__mf/js/async/{1964.75933dd4.js.LICENSE.txt → 1964.e6c5b93b.js.LICENSE.txt} +0 -0
  167. /package/__mf/js/async/{3090.90251187.js.LICENSE.txt → 2292.75e9aa11.js.LICENSE.txt} +0 -0
  168. /package/__mf/js/async/{1620.45989def.js.LICENSE.txt → 2981.8fe4ed12.js.LICENSE.txt} +0 -0
  169. /package/__mf/js/async/{2823.df67fd4b.js.LICENSE.txt → 3863.8d56ecec.js.LICENSE.txt} +0 -0
  170. /package/__mf/js/async/{3960.2228bf7e.js.LICENSE.txt → 3960.401ff0b0.js.LICENSE.txt} +0 -0
  171. /package/__mf/js/async/{3980.c94e78cd.js.LICENSE.txt → 3980.4d5490b2.js.LICENSE.txt} +0 -0
  172. /package/__mf/js/async/{3828.d981b319.js.LICENSE.txt → 4269.0cfaf9bb.js.LICENSE.txt} +0 -0
  173. /package/__mf/js/async/{5220.80e3b05e.js.LICENSE.txt → 5266.1c520126.js.LICENSE.txt} +0 -0
  174. /package/__mf/js/async/{5981.4700ddf6.js.LICENSE.txt → 5981.c6edce96.js.LICENSE.txt} +0 -0
  175. /package/__mf/js/async/{5207.e63b049c.js.LICENSE.txt → 6333.367d6758.js.LICENSE.txt} +0 -0
  176. /package/__mf/js/async/{5924.bfb4b2fd.js.LICENSE.txt → 6527.5341d09f.js.LICENSE.txt} +0 -0
  177. /package/__mf/js/async/{9173.83562213.js.LICENSE.txt → 9173.69dc268d.js.LICENSE.txt} +0 -0
  178. /package/__mf/js/async/{9478.57f45cd9.js.LICENSE.txt → 9314.4b8565a0.js.LICENSE.txt} +0 -0
  179. /package/__mf/js/async/{__federation_expose_TempoDatasource.4f96e206.js.LICENSE.txt → __federation_expose_TempoDatasource.fa3e24f9.js.LICENSE.txt} +0 -0
  180. /package/__mf/js/async/{__federation_expose_TempoExplorer.eb09c758.js.LICENSE.txt → __federation_expose_TempoExplorer.9949b25e.js.LICENSE.txt} +0 -0
  181. /package/__mf/js/async/{lib-router.46460b13.js.LICENSE.txt → lib-router.312ca028.js.LICENSE.txt} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/AttributeFilters.tsx"],"sourcesContent":["// Copyright 2025 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, SyntheticEvent, useCallback, useEffect, useState } from 'react';\nimport { Autocomplete, Checkbox, Stack, TextField, TextFieldProps } from '@mui/material';\nimport { isAbsoluteTimeRange, toAbsoluteTimeRange } from '@perses-dev/core';\nimport { useTimeRange } from '@perses-dev/plugin-system';\nimport { useQuery } from '@tanstack/react-query';\nimport CheckboxOutline from 'mdi-material-ui/CheckboxOutline';\nimport CheckboxBlankOutline from 'mdi-material-ui/CheckboxBlankOutline';\nimport { TempoClient } from '../model';\nimport { filterToTraceQL } from './filter/filter_to_traceql';\nimport { traceQLToFilter } from './filter/traceql_to_filter';\nimport { DurationField, Filter, splitByUnquotedWhitespace } from './filter/filter';\n\nconst statusOptions = ['unset', 'ok', 'error'];\n\nexport interface AttributeFiltersProps {\n client?: TempoClient;\n query: string;\n setQuery: (x: string) => void;\n}\n\nexport function AttributeFilters(props: AttributeFiltersProps): ReactElement {\n const { client, query, setQuery } = props;\n\n const filter = traceQLToFilter(query);\n const setFilter = (filter: Filter) => {\n setQuery(filterToTraceQL(filter));\n };\n\n const { timeRange } = useTimeRange();\n const absTimeRange = !isAbsoluteTimeRange(timeRange) ? toAbsoluteTimeRange(timeRange) : timeRange;\n const startTime = Math.round(absTimeRange.start.getTime() / 1000);\n const endTime = Math.round(absTimeRange.end.getTime() / 1000);\n\n const { data: serviceNameOptions } = useTagValues(\n client,\n 'resource.service.name',\n filterToTraceQL({ ...filter, serviceName: [] }),\n startTime,\n endTime\n );\n const { data: spanNameOptions } = useTagValues(\n client,\n 'name',\n filterToTraceQL({ ...filter, spanName: [] }),\n startTime,\n endTime\n );\n\n return (\n <>\n <StringAttributeFilter\n label=\"Service Name\"\n options={serviceNameOptions ?? []}\n value={filter.serviceName}\n setValue={(x) => setFilter({ ...filter, serviceName: x })}\n />\n <StringAttributeFilter\n label=\"Span Name\"\n options={spanNameOptions ?? []}\n value={filter.spanName}\n setValue={(x) => setFilter({ ...filter, spanName: x })}\n />\n <StringAttributeFilter\n label=\"Status\"\n width={210}\n options={statusOptions ?? []}\n value={filter.status}\n setValue={(x) => setFilter({ ...filter, status: x })}\n />\n <DurationAttributeFilter\n label=\"Trace Duration\"\n value={filter.traceDuration}\n setValue={(value) => setFilter({ ...filter, traceDuration: value })}\n />\n <CustomAttributesFilter\n label=\"Custom Attributes\"\n value={filter.customMatchers}\n setValue={(value) => setFilter({ ...filter, customMatchers: value })}\n />\n </>\n );\n}\n\ninterface StringAttributeFilterProps {\n label: string;\n width?: number;\n options: string[];\n value: string[];\n setValue: (value: string[]) => void;\n}\n\nconst checkboxBlankIcon = <CheckboxBlankOutline fontSize=\"small\" />;\nconst checkedMarkedIcon = <CheckboxOutline fontSize=\"small\" />;\n\nfunction StringAttributeFilter(props: StringAttributeFilterProps) {\n const { label, width, options, value, setValue } = props;\n\n return (\n <Autocomplete\n multiple\n size=\"small\"\n limitTags={1}\n disableCloseOnSelect\n value={value}\n onChange={(_event: SyntheticEvent, newValue: string[]) => setValue(newValue)}\n options={options}\n renderOption={(props, option, { selected }) => {\n const { key, ...optionProps } = props;\n return (\n <li key={key} {...optionProps}>\n <Checkbox\n icon={checkboxBlankIcon}\n checkedIcon={checkedMarkedIcon}\n style={{ marginRight: 8 }}\n checked={selected}\n />\n {option}\n </li>\n );\n }}\n renderInput={(params) => <TextField {...params} label={label} />}\n // Reduce the size of the chips to make space for the <input> element, the +X text and the X button to avoid a line break.\n // See https://github.com/mui/material-ui/issues/38835 for more details.\n slotProps={{ chip: { sx: { maxWidth: 'calc(100% - 45px) !important' } } }}\n // Reduce the size of the <input> field\n sx={{ width: width ?? 250, '& input': { minWidth: '5px !important' } }}\n />\n );\n}\n\ninterface DurationAttributeFilterProps {\n label: string;\n value: DurationField;\n setValue: (value: DurationField) => void;\n}\n\nfunction DurationAttributeFilter(props: DurationAttributeFilterProps) {\n const { label, value, setValue } = props;\n const { min, max } = value;\n\n return (\n <Stack direction=\"row\" gap={0.5}>\n <DurationTextInput label={`Min ${label}`} value={min ?? ''} setValue={(min) => setValue({ min, max })} />\n <DurationTextInput label={`Max ${label}`} value={max ?? ''} setValue={(max) => setValue({ min, max })} />\n </Stack>\n );\n}\n\nconst durationFormatRegex = /^([0-9]+\\.)?[0-9]+(ns|ms|s|m|h)$/;\n\ninterface DurationTextInputProps {\n label: string;\n value: string;\n setValue: (value: string) => void;\n}\n\nfunction DurationTextInput(props: DurationTextInputProps) {\n const { label, value, setValue } = props;\n\n return (\n <LazyTextInput\n label={label}\n size=\"small\"\n value={value}\n setValue={setValue}\n validationRegex={durationFormatRegex}\n validationFailedMessage=\"Invalid format. Accepted format e.g. 100ms, accepted units: ns, ms, s, m, h\"\n sx={{ width: 150 }}\n />\n );\n}\n\ninterface CustomAttributesFilterProps {\n label: string;\n value: string[];\n setValue: (value: string[]) => void;\n}\n\nfunction CustomAttributesFilter(props: CustomAttributesFilterProps) {\n const { label, value, setValue } = props;\n\n return (\n <LazyTextInput\n label={label}\n size=\"small\"\n placeholder='span.http.status_code=200 span.http.method=\"GET\"'\n value={value.join(' ')}\n setValue={(x) => setValue(splitByUnquotedWhitespace(x))}\n sx={{ flexGrow: 1 }}\n />\n );\n}\n\ninterface LazyTextInputProps extends Omit<TextFieldProps, 'variant'> {\n validationRegex?: RegExp;\n validationFailedMessage?: string;\n value: string;\n setValue: (value: string) => void;\n}\n\n/** A <TextField> which calls props.setValue when the input field is blurred and the validation passes. */\nfunction LazyTextInput(props: LazyTextInputProps) {\n const { validationRegex, validationFailedMessage, value, setValue, ...otherProps } = props;\n const [draftValue, setDraftValue] = useState(value);\n const isValidInput = draftValue == '' || validationRegex == undefined || validationRegex.test(draftValue);\n\n useEffect(() => {\n setDraftValue(value);\n }, [value, setDraftValue]);\n\n const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n setDraftValue(event.target.value);\n }, []);\n\n const handleBlur = useCallback(() => {\n if (isValidInput) {\n setValue(draftValue);\n }\n }, [isValidInput, setValue, draftValue]);\n\n return (\n <TextField\n {...otherProps}\n error={!isValidInput}\n helperText={isValidInput ? undefined : validationFailedMessage}\n value={draftValue}\n onChange={handleChange}\n onBlur={handleBlur}\n />\n );\n}\n\nfunction useTagValues(client: TempoClient | undefined, tag: string, query: string, start?: number, end?: number) {\n return useQuery({\n queryKey: ['useTagValues', client, tag, query, start, end],\n enabled: !!client,\n queryFn: async function () {\n if (!client) return;\n const values = await client.searchTagValues({ tag, q: query, start, end });\n return values.tagValues.map((tagValue) => tagValue.value ?? '').sort();\n },\n staleTime: 60 * 1000, // cache tag value response for 1m\n });\n}\n"],"names":["useCallback","useEffect","useState","Autocomplete","Checkbox","Stack","TextField","isAbsoluteTimeRange","toAbsoluteTimeRange","useTimeRange","useQuery","CheckboxOutline","CheckboxBlankOutline","filterToTraceQL","traceQLToFilter","splitByUnquotedWhitespace","statusOptions","AttributeFilters","props","client","query","setQuery","filter","setFilter","timeRange","absTimeRange","startTime","Math","round","start","getTime","endTime","end","data","serviceNameOptions","useTagValues","serviceName","spanNameOptions","spanName","StringAttributeFilter","label","options","value","setValue","x","width","status","DurationAttributeFilter","traceDuration","CustomAttributesFilter","customMatchers","checkboxBlankIcon","fontSize","checkedMarkedIcon","multiple","size","limitTags","disableCloseOnSelect","onChange","_event","newValue","renderOption","option","selected","key","optionProps","li","icon","checkedIcon","style","marginRight","checked","renderInput","params","slotProps","chip","sx","maxWidth","minWidth","min","max","direction","gap","DurationTextInput","durationFormatRegex","LazyTextInput","validationRegex","validationFailedMessage","placeholder","join","flexGrow","otherProps","draftValue","setDraftValue","isValidInput","undefined","test","handleChange","event","target","handleBlur","error","helperText","onBlur","tag","queryKey","enabled","queryFn","values","searchTagValues","q","tagValues","map","tagValue","sort","staleTime"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { ReactElement } from 'react';
2
2
  import { ReactCodeMirrorProps } from '@uiw/react-codemirror';
3
- import { CompletionConfig } from './TraceQLExtension';
3
+ import { TempoClient } from '../model';
4
4
  export interface TraceQLEditorProps extends Omit<ReactCodeMirrorProps, 'theme' | 'extensions'> {
5
- completionConfig: CompletionConfig;
5
+ client?: TempoClient;
6
6
  }
7
- export declare function TraceQLEditor({ completionConfig, ...rest }: TraceQLEditorProps): ReactElement;
7
+ export declare function TraceQLEditor({ client, ...rest }: TraceQLEditorProps): ReactElement;
8
8
  //# sourceMappingURL=TraceQLEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TraceQLEditor.d.ts","sourceRoot":"","sources":["../../../src/components/TraceQLEditor.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAE9C,OAAmB,EAAc,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAErF,OAAO,EAAE,gBAAgB,EAAoB,MAAM,oBAAoB,CAAC;AAExE,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,YAAY,CAAC;IAC5F,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,wBAAgB,aAAa,CAAC,EAAE,gBAAgB,EAAE,GAAG,IAAI,EAAE,EAAE,kBAAkB,GAAG,YAAY,CA4D7F"}
1
+ {"version":3,"file":"TraceQLEditor.d.ts","sourceRoot":"","sources":["../../../src/components/TraceQLEditor.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAE9C,OAAmB,EAAc,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGrF,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,YAAY,CAAC;IAC5F,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,kBAAkB,GAAG,YAAY,CA6DnF"}
@@ -15,14 +15,20 @@ import { useMemo } from 'react';
15
15
  import { InputLabel, Stack, useTheme } from '@mui/material';
16
16
  import CodeMirror, { EditorView } from '@uiw/react-codemirror';
17
17
  import { isValidTraceId } from '@perses-dev/core';
18
+ import { useTimeRange } from '@perses-dev/plugin-system';
18
19
  import { TraceQLExtension } from './TraceQLExtension';
19
- export function TraceQLEditor({ completionConfig, ...rest }) {
20
+ export function TraceQLEditor({ client, ...rest }) {
20
21
  const theme = useTheme();
21
22
  const isDarkMode = theme.palette.mode === 'dark';
23
+ const { timeRange } = useTimeRange();
22
24
  const traceQLExtension = useMemo(()=>{
23
- return TraceQLExtension(completionConfig);
25
+ return TraceQLExtension({
26
+ client,
27
+ timeRange
28
+ });
24
29
  }, [
25
- completionConfig
30
+ client,
31
+ timeRange
26
32
  ]);
27
33
  const codemirrorTheme = useMemo(()=>{
28
34
  // https://github.com/mui/material-ui/blob/v5.16.7/packages/mui-material/src/OutlinedInput/OutlinedInput.js#L43
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/TraceQLEditor.tsx"],"sourcesContent":["// Copyright 2025 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, useMemo } from 'react';\nimport { InputLabel, Stack, useTheme } from '@mui/material';\nimport CodeMirror, { EditorView, ReactCodeMirrorProps } from '@uiw/react-codemirror';\nimport { isValidTraceId } from '@perses-dev/core';\nimport { CompletionConfig, TraceQLExtension } from './TraceQLExtension';\n\nexport interface TraceQLEditorProps extends Omit<ReactCodeMirrorProps, 'theme' | 'extensions'> {\n completionConfig: CompletionConfig;\n}\n\nexport function TraceQLEditor({ completionConfig, ...rest }: TraceQLEditorProps): ReactElement {\n const theme = useTheme();\n const isDarkMode = theme.palette.mode === 'dark';\n\n const traceQLExtension = useMemo(() => {\n return TraceQLExtension(completionConfig);\n }, [completionConfig]);\n\n const codemirrorTheme = useMemo(() => {\n // https://github.com/mui/material-ui/blob/v5.16.7/packages/mui-material/src/OutlinedInput/OutlinedInput.js#L43\n const borderColor = theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)';\n\n return EditorView.theme({\n '&': {\n backgroundColor: 'transparent !important', // required for dark mode\n border: `1px solid ${borderColor}`,\n borderRadius: `${theme.shape.borderRadius}px`,\n },\n '&.cm-focused.cm-editor': {\n outline: 'none', // remove dotted outline on focus\n },\n '.cm-content': {\n padding: '8px',\n },\n });\n }, [theme]);\n\n return (\n <Stack position=\"relative\" sx={{ flexGrow: 1 }}>\n <InputLabel // reproduce the same kind of input label that regular MUI TextFields have\n shrink\n sx={{\n position: 'absolute',\n top: '-6px',\n left: '10px',\n padding: '0 4px',\n color: theme.palette.text.primary,\n backgroundColor: theme.palette.background.default,\n zIndex: 1,\n }}\n >\n TraceQL Expression\n </InputLabel>\n <CodeMirror\n {...rest}\n theme={isDarkMode ? 'dark' : 'light'}\n basicSetup={{\n lineNumbers: false,\n highlightActiveLine: false,\n highlightActiveLineGutter: false,\n foldGutter: false,\n // The explore view accepts either a TraceQL query or a Trace ID as input. The lezer grammar marks Trace IDs as invalid,\n // therefore let's disable syntax highlighting if the input is a Trace ID.\n syntaxHighlighting: !isValidTraceId(rest.value ?? ''),\n }}\n extensions={[EditorView.lineWrapping, traceQLExtension, codemirrorTheme]}\n placeholder='Example: {span.http.method = \"GET\"}'\n />\n </Stack>\n );\n}\n"],"names":["useMemo","InputLabel","Stack","useTheme","CodeMirror","EditorView","isValidTraceId","TraceQLExtension","TraceQLEditor","completionConfig","rest","theme","isDarkMode","palette","mode","traceQLExtension","codemirrorTheme","borderColor","backgroundColor","border","borderRadius","shape","outline","padding","position","sx","flexGrow","shrink","top","left","color","text","primary","background","default","zIndex","basicSetup","lineNumbers","highlightActiveLine","highlightActiveLineGutter","foldGutter","syntaxHighlighting","value","extensions","lineWrapping","placeholder"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAAuBA,OAAO,QAAQ,QAAQ;AAC9C,SAASC,UAAU,EAAEC,KAAK,EAAEC,QAAQ,QAAQ,gBAAgB;AAC5D,OAAOC,cAAcC,UAAU,QAA8B,wBAAwB;AACrF,SAASC,cAAc,QAAQ,mBAAmB;AAClD,SAA2BC,gBAAgB,QAAQ,qBAAqB;AAMxE,OAAO,SAASC,cAAc,EAAEC,gBAAgB,EAAE,GAAGC,MAA0B;IAC7E,MAAMC,QAAQR;IACd,MAAMS,aAAaD,MAAME,OAAO,CAACC,IAAI,KAAK;IAE1C,MAAMC,mBAAmBf,QAAQ;QAC/B,OAAOO,iBAAiBE;IAC1B,GAAG;QAACA;KAAiB;IAErB,MAAMO,kBAAkBhB,QAAQ;QAC9B,+GAA+G;QAC/G,MAAMiB,cAAcN,MAAME,OAAO,CAACC,IAAI,KAAK,UAAU,wBAAwB;QAE7E,OAAOT,WAAWM,KAAK,CAAC;YACtB,KAAK;gBACHO,iBAAiB;gBACjBC,QAAQ,CAAC,UAAU,EAAEF,aAAa;gBAClCG,cAAc,GAAGT,MAAMU,KAAK,CAACD,YAAY,CAAC,EAAE,CAAC;YAC/C;YACA,0BAA0B;gBACxBE,SAAS;YACX;YACA,eAAe;gBACbC,SAAS;YACX;QACF;IACF,GAAG;QAACZ;KAAM;IAEV,qBACE,MAACT;QAAMsB,UAAS;QAAWC,IAAI;YAAEC,UAAU;QAAE;;0BAC3C,KAACzB,WAAW,0EAA0E;;gBACpF0B,MAAM;gBACNF,IAAI;oBACFD,UAAU;oBACVI,KAAK;oBACLC,MAAM;oBACNN,SAAS;oBACTO,OAAOnB,MAAME,OAAO,CAACkB,IAAI,CAACC,OAAO;oBACjCd,iBAAiBP,MAAME,OAAO,CAACoB,UAAU,CAACC,OAAO;oBACjDC,QAAQ;gBACV;0BACD;;0BAGD,KAAC/B;gBACE,GAAGM,IAAI;gBACRC,OAAOC,aAAa,SAAS;gBAC7BwB,YAAY;oBACVC,aAAa;oBACbC,qBAAqB;oBACrBC,2BAA2B;oBAC3BC,YAAY;oBACZ,wHAAwH;oBACxH,0EAA0E;oBAC1EC,oBAAoB,CAACnC,eAAeI,KAAKgC,KAAK,IAAI;gBACpD;gBACAC,YAAY;oBAACtC,WAAWuC,YAAY;oBAAE7B;oBAAkBC;iBAAgB;gBACxE6B,aAAY;;;;AAIpB"}
1
+ {"version":3,"sources":["../../../src/components/TraceQLEditor.tsx"],"sourcesContent":["// Copyright 2025 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, useMemo } from 'react';\nimport { InputLabel, Stack, useTheme } from '@mui/material';\nimport CodeMirror, { EditorView, ReactCodeMirrorProps } from '@uiw/react-codemirror';\nimport { isValidTraceId } from '@perses-dev/core';\nimport { useTimeRange } from '@perses-dev/plugin-system';\nimport { TempoClient } from '../model';\nimport { TraceQLExtension } from './TraceQLExtension';\n\nexport interface TraceQLEditorProps extends Omit<ReactCodeMirrorProps, 'theme' | 'extensions'> {\n client?: TempoClient;\n}\n\nexport function TraceQLEditor({ client, ...rest }: TraceQLEditorProps): ReactElement {\n const theme = useTheme();\n const isDarkMode = theme.palette.mode === 'dark';\n\n const { timeRange } = useTimeRange();\n const traceQLExtension = useMemo(() => {\n return TraceQLExtension({ client, timeRange });\n }, [client, timeRange]);\n\n const codemirrorTheme = useMemo(() => {\n // https://github.com/mui/material-ui/blob/v5.16.7/packages/mui-material/src/OutlinedInput/OutlinedInput.js#L43\n const borderColor = theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)';\n\n return EditorView.theme({\n '&': {\n backgroundColor: 'transparent !important', // required for dark mode\n border: `1px solid ${borderColor}`,\n borderRadius: `${theme.shape.borderRadius}px`,\n },\n '&.cm-focused.cm-editor': {\n outline: 'none', // remove dotted outline on focus\n },\n '.cm-content': {\n padding: '8px',\n },\n });\n }, [theme]);\n\n return (\n <Stack position=\"relative\" sx={{ flexGrow: 1 }}>\n <InputLabel // reproduce the same kind of input label that regular MUI TextFields have\n shrink\n sx={{\n position: 'absolute',\n top: '-6px',\n left: '10px',\n padding: '0 4px',\n color: theme.palette.text.primary,\n backgroundColor: theme.palette.background.default,\n zIndex: 1,\n }}\n >\n TraceQL Expression\n </InputLabel>\n <CodeMirror\n {...rest}\n theme={isDarkMode ? 'dark' : 'light'}\n basicSetup={{\n lineNumbers: false,\n highlightActiveLine: false,\n highlightActiveLineGutter: false,\n foldGutter: false,\n // The explore view accepts either a TraceQL query or a Trace ID as input. The lezer grammar marks Trace IDs as invalid,\n // therefore let's disable syntax highlighting if the input is a Trace ID.\n syntaxHighlighting: !isValidTraceId(rest.value ?? ''),\n }}\n extensions={[EditorView.lineWrapping, traceQLExtension, codemirrorTheme]}\n placeholder='Example: {span.http.method = \"GET\"}'\n />\n </Stack>\n );\n}\n"],"names":["useMemo","InputLabel","Stack","useTheme","CodeMirror","EditorView","isValidTraceId","useTimeRange","TraceQLExtension","TraceQLEditor","client","rest","theme","isDarkMode","palette","mode","timeRange","traceQLExtension","codemirrorTheme","borderColor","backgroundColor","border","borderRadius","shape","outline","padding","position","sx","flexGrow","shrink","top","left","color","text","primary","background","default","zIndex","basicSetup","lineNumbers","highlightActiveLine","highlightActiveLineGutter","foldGutter","syntaxHighlighting","value","extensions","lineWrapping","placeholder"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAAuBA,OAAO,QAAQ,QAAQ;AAC9C,SAASC,UAAU,EAAEC,KAAK,EAAEC,QAAQ,QAAQ,gBAAgB;AAC5D,OAAOC,cAAcC,UAAU,QAA8B,wBAAwB;AACrF,SAASC,cAAc,QAAQ,mBAAmB;AAClD,SAASC,YAAY,QAAQ,4BAA4B;AAEzD,SAASC,gBAAgB,QAAQ,qBAAqB;AAMtD,OAAO,SAASC,cAAc,EAAEC,MAAM,EAAE,GAAGC,MAA0B;IACnE,MAAMC,QAAQT;IACd,MAAMU,aAAaD,MAAME,OAAO,CAACC,IAAI,KAAK;IAE1C,MAAM,EAAEC,SAAS,EAAE,GAAGT;IACtB,MAAMU,mBAAmBjB,QAAQ;QAC/B,OAAOQ,iBAAiB;YAAEE;YAAQM;QAAU;IAC9C,GAAG;QAACN;QAAQM;KAAU;IAEtB,MAAME,kBAAkBlB,QAAQ;QAC9B,+GAA+G;QAC/G,MAAMmB,cAAcP,MAAME,OAAO,CAACC,IAAI,KAAK,UAAU,wBAAwB;QAE7E,OAAOV,WAAWO,KAAK,CAAC;YACtB,KAAK;gBACHQ,iBAAiB;gBACjBC,QAAQ,CAAC,UAAU,EAAEF,aAAa;gBAClCG,cAAc,GAAGV,MAAMW,KAAK,CAACD,YAAY,CAAC,EAAE,CAAC;YAC/C;YACA,0BAA0B;gBACxBE,SAAS;YACX;YACA,eAAe;gBACbC,SAAS;YACX;QACF;IACF,GAAG;QAACb;KAAM;IAEV,qBACE,MAACV;QAAMwB,UAAS;QAAWC,IAAI;YAAEC,UAAU;QAAE;;0BAC3C,KAAC3B,WAAW,0EAA0E;;gBACpF4B,MAAM;gBACNF,IAAI;oBACFD,UAAU;oBACVI,KAAK;oBACLC,MAAM;oBACNN,SAAS;oBACTO,OAAOpB,MAAME,OAAO,CAACmB,IAAI,CAACC,OAAO;oBACjCd,iBAAiBR,MAAME,OAAO,CAACqB,UAAU,CAACC,OAAO;oBACjDC,QAAQ;gBACV;0BACD;;0BAGD,KAACjC;gBACE,GAAGO,IAAI;gBACRC,OAAOC,aAAa,SAAS;gBAC7ByB,YAAY;oBACVC,aAAa;oBACbC,qBAAqB;oBACrBC,2BAA2B;oBAC3BC,YAAY;oBACZ,wHAAwH;oBACxH,0EAA0E;oBAC1EC,oBAAoB,CAACrC,eAAeK,KAAKiC,KAAK,IAAI;gBACpD;gBACAC,YAAY;oBAACxC,WAAWyC,YAAY;oBAAE7B;oBAAkBC;iBAAgB;gBACxE6B,aAAY;;;;AAIpB"}
@@ -1,6 +1,7 @@
1
- import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
1
+ import { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
2
2
  import { EditorState } from '@codemirror/state';
3
3
  import { Tree } from '@lezer/common';
4
+ import { EditorView } from '@uiw/react-codemirror';
4
5
  import { CompletionConfig } from './TraceQLExtension';
5
6
  /** CompletionScope specifies the completion kind, e.g. whether to complete tag names or values etc. */
6
7
  type CompletionScope = {
@@ -31,5 +32,13 @@ export declare function complete(completionCfg: CompletionConfig, { state, pos }
31
32
  * Function is exported for tests only.
32
33
  */
33
34
  export declare function identifyCompletions(state: EditorState, pos: number, tree: Tree): Completions | undefined;
35
+ /**
36
+ * Add quotes to the completion text in case quotes are not present already.
37
+ * This handles the following cases:
38
+ * { name=HTTP
39
+ * { name="x
40
+ * { name="x" where cursor is after the 'x'
41
+ */
42
+ export declare function applyQuotedCompletion(view: EditorView, completion: Completion, from: number, to: number): void;
34
43
  export {};
35
44
  //# sourceMappingURL=complete.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/components/complete.ts"],"names":[],"mappings":"AAaA,OAAO,EAAc,iBAAiB,EAAE,gBAAgB,EAAwB,MAAM,0BAA0B,CAAC;AAEjH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAarC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,uGAAuG;AACvG,KAAK,eAAe,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,WAAW,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,QAAQ,CAC5B,aAAa,EAAE,gBAAgB,EAC/B,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,iBAAiB,GAChC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAWlC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,SAAS,CA4HxG"}
1
+ {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/components/complete.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAwB,MAAM,0BAA0B,CAAC;AAEjH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAWrC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,uGAAuG;AACvG,KAAK,eAAe,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,WAAW,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAKD,wBAAsB,QAAQ,CAC5B,aAAa,EAAE,gBAAgB,EAC/B,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,iBAAiB,GAChC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAWlC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,SAAS,CA4HxG;AAiED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAiB9G"}
@@ -14,6 +14,11 @@ import { insertCompletionText } from '@codemirror/autocomplete';
14
14
  import { syntaxTree } from '@codemirror/language';
15
15
  import { String as StringType, FieldExpression, AttributeField, Resource, Identifier, Span, SpansetFilter, FieldOp } from '@grafana/lezer-traceql';
16
16
  import { isAbsoluteTimeRange, toAbsoluteTimeRange } from '@perses-dev/core';
17
+ const quoteChars = [
18
+ '"',
19
+ '`'
20
+ ];
21
+ const defaultQuoteChar = '"';
17
22
  export async function complete(completionCfg, { state, pos }) {
18
23
  // First, identify the completion scopes, for example Scopes() and TagName(scope=intrinsic)
19
24
  const completions = identifyCompletions(state, pos, syntaxTree(state));
@@ -212,7 +217,7 @@ export async function complete(completionCfg, { state, pos }) {
212
217
  const fieldExpr = node.parent.firstChild;
213
218
  const attribute = state.sliceDoc(fieldExpr.from, fieldExpr.to);
214
219
  // ignore leading " in { name="HT
215
- const from = state.sliceDoc(node.from, node.from + 1) === '"' ? node.from + 1 : node.from;
220
+ const from = quoteChars.includes(state.sliceDoc(node.from, node.from + 1)) ? node.from + 1 : node.from;
216
221
  return {
217
222
  scopes: [
218
223
  {
@@ -300,19 +305,33 @@ async function completeTagName(completionCfg, scope) {
300
305
  label: tag
301
306
  }));
302
307
  }
308
+ function escapeString(input, quoteChar) {
309
+ let escaped = input;
310
+ escaped = escaped.replaceAll('\\', '\\\\');
311
+ if (quoteChar == '"') {
312
+ escaped = escaped.replaceAll('"', '\\"');
313
+ }
314
+ return escaped;
315
+ }
303
316
  /**
304
317
  * Add quotes to the completion text in case quotes are not present already.
305
318
  * This handles the following cases:
306
319
  * { name=HTTP
307
320
  * { name="x
308
321
  * { name="x" where cursor is after the 'x'
309
- */ function applyQuotedCompletion(view, completion, from, to) {
310
- let insertText = completion.label;
311
- if (view.state.sliceDoc(from - 1, from) !== '"') {
312
- insertText = '"' + insertText;
322
+ */ export function applyQuotedCompletion(view, completion, from, to) {
323
+ let quoteChar = defaultQuoteChar;
324
+ if (quoteChars.includes(view.state.sliceDoc(from - 1, from))) {
325
+ quoteChar = view.state.sliceDoc(from - 1, from);
326
+ } else if (quoteChars.includes(view.state.sliceDoc(to, to + 1))) {
327
+ quoteChar = view.state.sliceDoc(to, to + 1);
328
+ }
329
+ let insertText = escapeString(completion.label, quoteChar);
330
+ if (view.state.sliceDoc(from - 1, from) !== quoteChar) {
331
+ insertText = quoteChar + insertText;
313
332
  }
314
- if (view.state.sliceDoc(to, to + 1) !== '"') {
315
- insertText = insertText + '"';
333
+ if (view.state.sliceDoc(to, to + 1) !== quoteChar) {
334
+ insertText = insertText + quoteChar;
316
335
  }
317
336
  view.dispatch(insertCompletionText(view.state, insertText, from, to));
318
337
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/complete.ts"],"sourcesContent":["// Copyright 2024 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Completion, CompletionContext, CompletionResult, insertCompletionText } from '@codemirror/autocomplete';\nimport { syntaxTree } from '@codemirror/language';\nimport { EditorState } from '@codemirror/state';\nimport { Tree } from '@lezer/common';\nimport {\n String as StringType,\n FieldExpression,\n AttributeField,\n Resource,\n Identifier,\n Span,\n SpansetFilter,\n FieldOp,\n} from '@grafana/lezer-traceql';\nimport { EditorView } from '@uiw/react-codemirror';\nimport { isAbsoluteTimeRange, TimeRangeValue, toAbsoluteTimeRange } from '@perses-dev/core';\nimport { CompletionConfig } from './TraceQLExtension';\n\n/** CompletionScope specifies the completion kind, e.g. whether to complete tag names or values etc. */\ntype CompletionScope =\n | { kind: 'Scopes' } // 'resource'|'span'\n | { kind: 'TagName'; scope: 'resource' | 'span' | 'intrinsic' }\n | { kind: 'TagValue'; tag: string };\n\n/**\n * Completions specifies the identified scopes and position of the completion in the current editor text.\n * For example, when entering '{' the following completions are possible: Scopes(), TagName(scope=intrinsic)\n */\nexport interface Completions {\n scopes: CompletionScope[];\n from: number;\n to?: number;\n}\n\nexport async function complete(\n completionCfg: CompletionConfig,\n { state, pos }: CompletionContext\n): Promise<CompletionResult | null> {\n // First, identify the completion scopes, for example Scopes() and TagName(scope=intrinsic)\n const completions = identifyCompletions(state, pos, syntaxTree(state));\n if (!completions) {\n // No completion scopes found for current cursor position.\n return null;\n }\n\n // Then, retrieve completion options for all identified scopes (from the Tempo API).\n const options = await retrieveOptions(completionCfg, completions.scopes);\n return { options, from: completions.from, to: completions.to };\n}\n\n/**\n * Identify completion scopes (e.g. TagValue) and position, based on the current node in the syntax tree.\n *\n * For development, you can visualize the tree of a TraceQL query using this tool:\n * https://github.com/grafana/lezer-traceql/blob/main/tools/tree-viz.html\n *\n * Function is exported for tests only.\n */\nexport function identifyCompletions(state: EditorState, pos: number, tree: Tree): Completions | undefined {\n const node = tree.resolveInner(pos, -1);\n\n switch (node.type.id) {\n case SpansetFilter:\n // autocomplete {\n // autocomplete {}\n // do not autocomplete if cursor is after } or { status=ok }\n if (\n (node.firstChild === null || node.firstChild?.type.id === 0) &&\n !state.sliceDoc(node.from, pos).includes('}')\n ) {\n return {\n scopes: [{ kind: 'Scopes' }, { kind: 'TagName', scope: 'intrinsic' }],\n from: pos,\n };\n }\n break;\n\n case FieldExpression:\n // autocomplete { status=ok &&\n return {\n scopes: [{ kind: 'Scopes' }, { kind: 'TagName', scope: 'intrinsic' }],\n from: pos,\n };\n\n case AttributeField:\n // autocomplete { resource.\n if (node.firstChild?.type.id === Resource) {\n return { scopes: [{ kind: 'TagName', scope: 'resource' }], from: pos };\n }\n\n // autocomplete { span.\n if (node.firstChild?.type.id === Span) {\n return { scopes: [{ kind: 'TagName', scope: 'span' }], from: pos };\n }\n\n // autocomplete { .\n if (state.sliceDoc(node.from, node.to) === '.') {\n return {\n scopes: [\n { kind: 'TagName', scope: 'resource' },\n { kind: 'TagName', scope: 'span' },\n ],\n from: pos,\n };\n }\n break;\n\n case Identifier:\n if (node.parent?.type.id === AttributeField) {\n const text = state.sliceDoc(node.parent.from, node.parent.to);\n // autocomplete { span:s\n // only intrinsic fields can have a : in the name.\n if (text.includes(':')) {\n return { scopes: [{ kind: 'TagName', scope: 'intrinsic' }], from: node.parent.from };\n }\n\n // autocomplete { resource.s\n if (node.parent?.firstChild?.type.id === Resource) {\n return { scopes: [{ kind: 'TagName', scope: 'resource' }], from: node.from };\n }\n\n // autocomplete { span.s\n if (node.parent?.firstChild?.type.id === Span) {\n return { scopes: [{ kind: 'TagName', scope: 'span' }], from: node.from };\n }\n\n // autocomplete { .s\n if (node.parent?.firstChild?.type.id === Identifier) {\n return {\n scopes: [\n { kind: 'TagName', scope: 'resource' },\n { kind: 'TagName', scope: 'span' },\n ],\n from: node.from,\n };\n }\n }\n break;\n\n case FieldOp:\n // autocomplete { status=\n // autocomplete { span.http.method=\n if (node.parent?.firstChild?.type.id === FieldExpression) {\n const fieldExpr = node.parent.firstChild;\n const attribute = state.sliceDoc(fieldExpr.from, fieldExpr.to);\n return { scopes: [{ kind: 'TagValue', tag: attribute }], from: pos };\n }\n break;\n\n case StringType:\n // autocomplete { resource.service.name=\"\n // do not autocomplete if cursor is after closing quotes { resource.service.name=\"\"\n if (\n node.parent?.parent?.parent?.firstChild?.type.id === FieldExpression &&\n !/^\".*\"$/.test(state.sliceDoc(node.from, pos))\n ) {\n const fieldExpr = node.parent.parent.parent.firstChild;\n const attribute = state.sliceDoc(fieldExpr.from, fieldExpr.to);\n return { scopes: [{ kind: 'TagValue', tag: attribute }], from: node.from + 1 }; // node.from+1 to ignore leading \"\n }\n break;\n\n case 0 /* error node */:\n // autocomplete { status=e\n if (node.prevSibling?.type.id === FieldOp && node.parent?.firstChild?.type.id === FieldExpression) {\n const fieldExpr = node.parent.firstChild;\n const attribute = state.sliceDoc(fieldExpr.from, fieldExpr.to);\n // ignore leading \" in { name=\"HT\n const from = state.sliceDoc(node.from, node.from + 1) === '\"' ? node.from + 1 : node.from;\n return { scopes: [{ kind: 'TagValue', tag: attribute }], from };\n }\n\n // autocomplete { s\n // autocomplete { status=ok && s\n if (node.parent?.type.id === SpansetFilter || node.parent?.type.id === FieldExpression) {\n return {\n scopes: [{ kind: 'Scopes' }, { kind: 'TagName', scope: 'intrinsic' }],\n from: node.from,\n };\n }\n break;\n }\n}\n\n/**\n * Retrieve all completion options based on the previously identified completion scopes.\n */\nasync function retrieveOptions(completionCfg: CompletionConfig, completions: CompletionScope[]): Promise<Completion[]> {\n const results: Array<Promise<Completion[]>> = [];\n\n for (const completion of completions) {\n switch (completion.kind) {\n case 'Scopes':\n results.push(Promise.resolve([{ label: 'span' }, { label: 'resource' }]));\n break;\n\n case 'TagName':\n results.push(completeTagName(completionCfg, completion.scope));\n break;\n\n case 'TagValue':\n results.push(completeTagValue(completionCfg, completion.tag));\n break;\n }\n }\n\n // Retrieve options concurrently\n // e.g. for unscoped attribute fields, retrieve list of span and resource attributes concurrently.\n const options = await Promise.all(results);\n return options.flat();\n}\n\nfunction getUnixTimeRange(timeRange?: TimeRangeValue): { start?: number; end?: number } {\n if (!timeRange) {\n return {};\n }\n\n const absTimeRange = !isAbsoluteTimeRange(timeRange) ? toAbsoluteTimeRange(timeRange) : timeRange;\n const start = Math.round(absTimeRange.start.getTime() / 1000);\n const end = Math.round(absTimeRange.end.getTime() / 1000);\n return { start, end };\n}\n\nasync function completeTagName(\n completionCfg: CompletionConfig,\n scope: 'resource' | 'span' | 'intrinsic'\n): Promise<Completion[]> {\n if (!completionCfg.client) {\n return [];\n }\n\n const { start, end } = getUnixTimeRange(completionCfg.timeRange);\n const { limit, maxStaleValues } = completionCfg;\n\n const response = await completionCfg.client.searchTags({ scope, start, end, limit, maxStaleValues });\n return response.scopes.flatMap((scope) => scope.tags).map((tag) => ({ label: tag }));\n}\n\n/**\n * Add quotes to the completion text in case quotes are not present already.\n * This handles the following cases:\n * { name=HTTP\n * { name=\"x\n * { name=\"x\" where cursor is after the 'x'\n */\nfunction applyQuotedCompletion(view: EditorView, completion: Completion, from: number, to: number): void {\n let insertText = completion.label;\n if (view.state.sliceDoc(from - 1, from) !== '\"') {\n insertText = '\"' + insertText;\n }\n if (view.state.sliceDoc(to, to + 1) !== '\"') {\n insertText = insertText + '\"';\n }\n view.dispatch(insertCompletionText(view.state, insertText, from, to));\n}\n\nasync function completeTagValue(completionCfg: CompletionConfig, tag: string): Promise<Completion[]> {\n if (!completionCfg.client) {\n return [];\n }\n\n const { start, end } = getUnixTimeRange(completionCfg.timeRange);\n const { limit, maxStaleValues } = completionCfg;\n\n const response = await completionCfg.client.searchTagValues({ tag, start, end, limit, maxStaleValues });\n const completions: Completion[] = [];\n for (const { type, value } of response.tagValues) {\n switch (type) {\n case 'string':\n completions.push({ label: value ?? '', displayLabel: value ?? '(empty string)', apply: applyQuotedCompletion });\n break;\n\n case 'keyword':\n case 'int':\n completions.push({ label: value ?? '', displayLabel: value ?? '(empty string)' });\n break;\n }\n }\n return completions;\n}\n"],"names":["insertCompletionText","syntaxTree","String","StringType","FieldExpression","AttributeField","Resource","Identifier","Span","SpansetFilter","FieldOp","isAbsoluteTimeRange","toAbsoluteTimeRange","complete","completionCfg","state","pos","completions","identifyCompletions","options","retrieveOptions","scopes","from","to","tree","node","resolveInner","type","id","firstChild","sliceDoc","includes","kind","scope","parent","text","fieldExpr","attribute","tag","test","prevSibling","results","completion","push","Promise","resolve","label","completeTagName","completeTagValue","all","flat","getUnixTimeRange","timeRange","absTimeRange","start","Math","round","getTime","end","client","limit","maxStaleValues","response","searchTags","flatMap","tags","map","applyQuotedCompletion","view","insertText","dispatch","searchTagValues","value","tagValues","displayLabel","apply"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/complete.ts"],"sourcesContent":["// Copyright 2024 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Completion, CompletionContext, CompletionResult, insertCompletionText } from '@codemirror/autocomplete';\nimport { syntaxTree } from '@codemirror/language';\nimport { EditorState } from '@codemirror/state';\nimport { Tree } from '@lezer/common';\nimport {\n String as StringType,\n FieldExpression,\n AttributeField,\n Resource,\n Identifier,\n Span,\n SpansetFilter,\n FieldOp,\n} from '@grafana/lezer-traceql';\nimport { EditorView } from '@uiw/react-codemirror';\nimport { isAbsoluteTimeRange, TimeRangeValue, toAbsoluteTimeRange } from '@perses-dev/core';\nimport { CompletionConfig } from './TraceQLExtension';\n\n/** CompletionScope specifies the completion kind, e.g. whether to complete tag names or values etc. */\ntype CompletionScope =\n | { kind: 'Scopes' } // 'resource'|'span'\n | { kind: 'TagName'; scope: 'resource' | 'span' | 'intrinsic' }\n | { kind: 'TagValue'; tag: string };\n\n/**\n * Completions specifies the identified scopes and position of the completion in the current editor text.\n * For example, when entering '{' the following completions are possible: Scopes(), TagName(scope=intrinsic)\n */\nexport interface Completions {\n scopes: CompletionScope[];\n from: number;\n to?: number;\n}\n\nconst quoteChars = ['\"', '`'];\nconst defaultQuoteChar = '\"';\n\nexport async function complete(\n completionCfg: CompletionConfig,\n { state, pos }: CompletionContext\n): Promise<CompletionResult | null> {\n // First, identify the completion scopes, for example Scopes() and TagName(scope=intrinsic)\n const completions = identifyCompletions(state, pos, syntaxTree(state));\n if (!completions) {\n // No completion scopes found for current cursor position.\n return null;\n }\n\n // Then, retrieve completion options for all identified scopes (from the Tempo API).\n const options = await retrieveOptions(completionCfg, completions.scopes);\n return { options, from: completions.from, to: completions.to };\n}\n\n/**\n * Identify completion scopes (e.g. TagValue) and position, based on the current node in the syntax tree.\n *\n * For development, you can visualize the tree of a TraceQL query using this tool:\n * https://github.com/grafana/lezer-traceql/blob/main/tools/tree-viz.html\n *\n * Function is exported for tests only.\n */\nexport function identifyCompletions(state: EditorState, pos: number, tree: Tree): Completions | undefined {\n const node = tree.resolveInner(pos, -1);\n\n switch (node.type.id) {\n case SpansetFilter:\n // autocomplete {\n // autocomplete {}\n // do not autocomplete if cursor is after } or { status=ok }\n if (\n (node.firstChild === null || node.firstChild?.type.id === 0) &&\n !state.sliceDoc(node.from, pos).includes('}')\n ) {\n return {\n scopes: [{ kind: 'Scopes' }, { kind: 'TagName', scope: 'intrinsic' }],\n from: pos,\n };\n }\n break;\n\n case FieldExpression:\n // autocomplete { status=ok &&\n return {\n scopes: [{ kind: 'Scopes' }, { kind: 'TagName', scope: 'intrinsic' }],\n from: pos,\n };\n\n case AttributeField:\n // autocomplete { resource.\n if (node.firstChild?.type.id === Resource) {\n return { scopes: [{ kind: 'TagName', scope: 'resource' }], from: pos };\n }\n\n // autocomplete { span.\n if (node.firstChild?.type.id === Span) {\n return { scopes: [{ kind: 'TagName', scope: 'span' }], from: pos };\n }\n\n // autocomplete { .\n if (state.sliceDoc(node.from, node.to) === '.') {\n return {\n scopes: [\n { kind: 'TagName', scope: 'resource' },\n { kind: 'TagName', scope: 'span' },\n ],\n from: pos,\n };\n }\n break;\n\n case Identifier:\n if (node.parent?.type.id === AttributeField) {\n const text = state.sliceDoc(node.parent.from, node.parent.to);\n // autocomplete { span:s\n // only intrinsic fields can have a : in the name.\n if (text.includes(':')) {\n return { scopes: [{ kind: 'TagName', scope: 'intrinsic' }], from: node.parent.from };\n }\n\n // autocomplete { resource.s\n if (node.parent?.firstChild?.type.id === Resource) {\n return { scopes: [{ kind: 'TagName', scope: 'resource' }], from: node.from };\n }\n\n // autocomplete { span.s\n if (node.parent?.firstChild?.type.id === Span) {\n return { scopes: [{ kind: 'TagName', scope: 'span' }], from: node.from };\n }\n\n // autocomplete { .s\n if (node.parent?.firstChild?.type.id === Identifier) {\n return {\n scopes: [\n { kind: 'TagName', scope: 'resource' },\n { kind: 'TagName', scope: 'span' },\n ],\n from: node.from,\n };\n }\n }\n break;\n\n case FieldOp:\n // autocomplete { status=\n // autocomplete { span.http.method=\n if (node.parent?.firstChild?.type.id === FieldExpression) {\n const fieldExpr = node.parent.firstChild;\n const attribute = state.sliceDoc(fieldExpr.from, fieldExpr.to);\n return { scopes: [{ kind: 'TagValue', tag: attribute }], from: pos };\n }\n break;\n\n case StringType:\n // autocomplete { resource.service.name=\"\n // do not autocomplete if cursor is after closing quotes { resource.service.name=\"\"\n if (\n node.parent?.parent?.parent?.firstChild?.type.id === FieldExpression &&\n !/^\".*\"$/.test(state.sliceDoc(node.from, pos))\n ) {\n const fieldExpr = node.parent.parent.parent.firstChild;\n const attribute = state.sliceDoc(fieldExpr.from, fieldExpr.to);\n return { scopes: [{ kind: 'TagValue', tag: attribute }], from: node.from + 1 }; // node.from+1 to ignore leading \"\n }\n break;\n\n case 0 /* error node */:\n // autocomplete { status=e\n if (node.prevSibling?.type.id === FieldOp && node.parent?.firstChild?.type.id === FieldExpression) {\n const fieldExpr = node.parent.firstChild;\n const attribute = state.sliceDoc(fieldExpr.from, fieldExpr.to);\n // ignore leading \" in { name=\"HT\n const from = quoteChars.includes(state.sliceDoc(node.from, node.from + 1)) ? node.from + 1 : node.from;\n return { scopes: [{ kind: 'TagValue', tag: attribute }], from };\n }\n\n // autocomplete { s\n // autocomplete { status=ok && s\n if (node.parent?.type.id === SpansetFilter || node.parent?.type.id === FieldExpression) {\n return {\n scopes: [{ kind: 'Scopes' }, { kind: 'TagName', scope: 'intrinsic' }],\n from: node.from,\n };\n }\n break;\n }\n}\n\n/**\n * Retrieve all completion options based on the previously identified completion scopes.\n */\nasync function retrieveOptions(completionCfg: CompletionConfig, completions: CompletionScope[]): Promise<Completion[]> {\n const results: Array<Promise<Completion[]>> = [];\n\n for (const completion of completions) {\n switch (completion.kind) {\n case 'Scopes':\n results.push(Promise.resolve([{ label: 'span' }, { label: 'resource' }]));\n break;\n\n case 'TagName':\n results.push(completeTagName(completionCfg, completion.scope));\n break;\n\n case 'TagValue':\n results.push(completeTagValue(completionCfg, completion.tag));\n break;\n }\n }\n\n // Retrieve options concurrently\n // e.g. for unscoped attribute fields, retrieve list of span and resource attributes concurrently.\n const options = await Promise.all(results);\n return options.flat();\n}\n\nfunction getUnixTimeRange(timeRange?: TimeRangeValue): { start?: number; end?: number } {\n if (!timeRange) {\n return {};\n }\n\n const absTimeRange = !isAbsoluteTimeRange(timeRange) ? toAbsoluteTimeRange(timeRange) : timeRange;\n const start = Math.round(absTimeRange.start.getTime() / 1000);\n const end = Math.round(absTimeRange.end.getTime() / 1000);\n return { start, end };\n}\n\nasync function completeTagName(\n completionCfg: CompletionConfig,\n scope: 'resource' | 'span' | 'intrinsic'\n): Promise<Completion[]> {\n if (!completionCfg.client) {\n return [];\n }\n\n const { start, end } = getUnixTimeRange(completionCfg.timeRange);\n const { limit, maxStaleValues } = completionCfg;\n\n const response = await completionCfg.client.searchTags({ scope, start, end, limit, maxStaleValues });\n return response.scopes.flatMap((scope) => scope.tags).map((tag) => ({ label: tag }));\n}\n\nfunction escapeString(input: string, quoteChar: string) {\n let escaped = input;\n escaped = escaped.replaceAll('\\\\', '\\\\\\\\');\n if (quoteChar == '\"') {\n escaped = escaped.replaceAll('\"', '\\\\\"');\n }\n return escaped;\n}\n\n/**\n * Add quotes to the completion text in case quotes are not present already.\n * This handles the following cases:\n * { name=HTTP\n * { name=\"x\n * { name=\"x\" where cursor is after the 'x'\n */\nexport function applyQuotedCompletion(view: EditorView, completion: Completion, from: number, to: number): void {\n let quoteChar = defaultQuoteChar;\n if (quoteChars.includes(view.state.sliceDoc(from - 1, from))) {\n quoteChar = view.state.sliceDoc(from - 1, from);\n } else if (quoteChars.includes(view.state.sliceDoc(to, to + 1))) {\n quoteChar = view.state.sliceDoc(to, to + 1);\n }\n\n let insertText = escapeString(completion.label, quoteChar);\n\n if (view.state.sliceDoc(from - 1, from) !== quoteChar) {\n insertText = quoteChar + insertText;\n }\n if (view.state.sliceDoc(to, to + 1) !== quoteChar) {\n insertText = insertText + quoteChar;\n }\n view.dispatch(insertCompletionText(view.state, insertText, from, to));\n}\n\nasync function completeTagValue(completionCfg: CompletionConfig, tag: string): Promise<Completion[]> {\n if (!completionCfg.client) {\n return [];\n }\n\n const { start, end } = getUnixTimeRange(completionCfg.timeRange);\n const { limit, maxStaleValues } = completionCfg;\n\n const response = await completionCfg.client.searchTagValues({ tag, start, end, limit, maxStaleValues });\n const completions: Completion[] = [];\n for (const { type, value } of response.tagValues) {\n switch (type) {\n case 'string':\n completions.push({ label: value ?? '', displayLabel: value ?? '(empty string)', apply: applyQuotedCompletion });\n break;\n\n case 'keyword':\n case 'int':\n completions.push({ label: value ?? '', displayLabel: value ?? '(empty string)' });\n break;\n }\n }\n return completions;\n}\n"],"names":["insertCompletionText","syntaxTree","String","StringType","FieldExpression","AttributeField","Resource","Identifier","Span","SpansetFilter","FieldOp","isAbsoluteTimeRange","toAbsoluteTimeRange","quoteChars","defaultQuoteChar","complete","completionCfg","state","pos","completions","identifyCompletions","options","retrieveOptions","scopes","from","to","tree","node","resolveInner","type","id","firstChild","sliceDoc","includes","kind","scope","parent","text","fieldExpr","attribute","tag","test","prevSibling","results","completion","push","Promise","resolve","label","completeTagName","completeTagValue","all","flat","getUnixTimeRange","timeRange","absTimeRange","start","Math","round","getTime","end","client","limit","maxStaleValues","response","searchTags","flatMap","tags","map","escapeString","input","quoteChar","escaped","replaceAll","applyQuotedCompletion","view","insertText","dispatch","searchTagValues","value","tagValues","displayLabel","apply"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /** A model of a filter bar for common tracing attributes. All attributes are combined with AND, the values of an attribute are combined with OR. */
2
+ export interface Filter {
3
+ serviceName: string[];
4
+ spanName: string[];
5
+ namespace: string[];
6
+ status: string[];
7
+ spanDuration: DurationField;
8
+ traceDuration: DurationField;
9
+ customMatchers: string[];
10
+ }
11
+ export interface DurationField {
12
+ min?: string;
13
+ max?: string;
14
+ }
15
+ /** split a string by whitespace, except when inside quotes */
16
+ export declare function splitByUnquotedWhitespace(x: string): string[];
17
+ //# sourceMappingURL=filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../../../src/components/filter/filter.ts"],"names":[],"mappings":"AAAA,oJAAoJ;AACpJ,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,aAAa,CAAC;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,8DAA8D;AAC9D,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,MAAM,YAclD"}
@@ -0,0 +1,17 @@
1
+ /** A model of a filter bar for common tracing attributes. All attributes are combined with AND, the values of an attribute are combined with OR. */ /** split a string by whitespace, except when inside quotes */ export function splitByUnquotedWhitespace(x) {
2
+ let quote = false;
3
+ let from = 0;
4
+ const chunks = [];
5
+ for(let i = 0; i < x.length; i++){
6
+ if (x[i] == '"' && x[i - 1] != '\\') {
7
+ quote = !quote;
8
+ } else if (x[i] == ' ' && !quote) {
9
+ chunks.push(x.slice(from, i));
10
+ from = i + 1;
11
+ }
12
+ }
13
+ chunks.push(x.slice(from, x.length));
14
+ return chunks.filter((x)=>x);
15
+ }
16
+
17
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/filter/filter.ts"],"sourcesContent":["/** A model of a filter bar for common tracing attributes. All attributes are combined with AND, the values of an attribute are combined with OR. */\nexport interface Filter {\n serviceName: string[];\n spanName: string[];\n namespace: string[];\n status: string[];\n spanDuration: DurationField;\n traceDuration: DurationField;\n customMatchers: string[];\n}\n\nexport interface DurationField {\n min?: string;\n max?: string;\n}\n\n/** split a string by whitespace, except when inside quotes */\nexport function splitByUnquotedWhitespace(x: string) {\n let quote = false;\n let from = 0;\n const chunks: string[] = [];\n for (let i = 0; i < x.length; i++) {\n if (x[i] == '\"' && x[i - 1] != '\\\\') {\n quote = !quote;\n } else if (x[i] == ' ' && !quote) {\n chunks.push(x.slice(from, i));\n from = i + 1;\n }\n }\n chunks.push(x.slice(from, x.length));\n return chunks.filter((x) => x);\n}\n"],"names":["splitByUnquotedWhitespace","x","quote","from","chunks","i","length","push","slice","filter"],"mappings":"AAAA,kJAAkJ,GAgBlJ,4DAA4D,GAC5D,OAAO,SAASA,0BAA0BC,CAAS;IACjD,IAAIC,QAAQ;IACZ,IAAIC,OAAO;IACX,MAAMC,SAAmB,EAAE;IAC3B,IAAK,IAAIC,IAAI,GAAGA,IAAIJ,EAAEK,MAAM,EAAED,IAAK;QACjC,IAAIJ,CAAC,CAACI,EAAE,IAAI,OAAOJ,CAAC,CAACI,IAAI,EAAE,IAAI,MAAM;YACnCH,QAAQ,CAACA;QACX,OAAO,IAAID,CAAC,CAACI,EAAE,IAAI,OAAO,CAACH,OAAO;YAChCE,OAAOG,IAAI,CAACN,EAAEO,KAAK,CAACL,MAAME;YAC1BF,OAAOE,IAAI;QACb;IACF;IACAD,OAAOG,IAAI,CAACN,EAAEO,KAAK,CAACL,MAAMF,EAAEK,MAAM;IAClC,OAAOF,OAAOK,MAAM,CAAC,CAACR,IAAMA;AAC9B"}
@@ -0,0 +1,9 @@
1
+ import { Filter } from './filter';
2
+ /**
3
+ * Construct a TraceQL query from a filter.
4
+ * 1. Creates the matchers, for example 'resource.service.name = "some_value"' or 'resource.service.name =~ "some_value|other_value"'
5
+ * 2. Join all matchers with '&&'
6
+ * 3. Return the full TraceQL query, for example '{ resource.service.name = "some_value" && name = "span_name" }'
7
+ */
8
+ export declare function filterToTraceQL(filter: Filter): string;
9
+ //# sourceMappingURL=filter_to_traceql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter_to_traceql.d.ts","sourceRoot":"","sources":["../../../../src/components/filter/filter_to_traceql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,UAe7C"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Construct a TraceQL query from a filter.
3
+ * 1. Creates the matchers, for example 'resource.service.name = "some_value"' or 'resource.service.name =~ "some_value|other_value"'
4
+ * 2. Join all matchers with '&&'
5
+ * 3. Return the full TraceQL query, for example '{ resource.service.name = "some_value" && name = "span_name" }'
6
+ */ export function filterToTraceQL(filter) {
7
+ const matchers = [
8
+ ...stringMatcher('resource.service.name', filter.serviceName),
9
+ ...stringMatcher('name', filter.spanName),
10
+ ...stringMatcher('resource.k8s.namespace.name', filter.namespace),
11
+ ...intrinsicMatcher('status', filter.status),
12
+ ...durationMatcher('duration', filter.spanDuration),
13
+ ...durationMatcher('traceDuration', filter.traceDuration),
14
+ ...customMatcher(filter.customMatchers)
15
+ ];
16
+ if (matchers.length === 0) {
17
+ return '{}';
18
+ }
19
+ return `{ ${matchers.join(' && ')} }`;
20
+ }
21
+ function escape(q) {
22
+ return q.replaceAll('\\', '\\\\').replaceAll('"', '\\"');
23
+ }
24
+ function stringMatcher(attribute, values) {
25
+ const escapedValues = values.map(escape);
26
+ if (escapedValues.length > 1) {
27
+ return [
28
+ `${attribute} =~ "${escapedValues.join('|')}"`
29
+ ];
30
+ } else if (escapedValues.length == 1) {
31
+ return [
32
+ `${attribute} = "${escapedValues[0]}"`
33
+ ];
34
+ }
35
+ return [];
36
+ }
37
+ function intrinsicMatcher(attribute, values) {
38
+ const orConds = values.map((x)=>`${attribute} = ${x}`);
39
+ if (orConds.length > 1) {
40
+ return [
41
+ '(' + orConds.join(' || ') + ')'
42
+ ];
43
+ } else if (orConds.length === 1) {
44
+ return orConds;
45
+ } else {
46
+ return [];
47
+ }
48
+ }
49
+ function durationMatcher(attribute, value) {
50
+ const matchers = [];
51
+ if (value.min) {
52
+ matchers.push(`${attribute} >= ${value.min}`);
53
+ }
54
+ if (value.max) {
55
+ matchers.push(`${attribute} <= ${value.max}`);
56
+ }
57
+ return matchers;
58
+ }
59
+ function customMatcher(customMatchers) {
60
+ return customMatchers;
61
+ }
62
+
63
+ //# sourceMappingURL=filter_to_traceql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/filter/filter_to_traceql.ts"],"sourcesContent":["import { DurationField, Filter } from './filter';\n\n/**\n * Construct a TraceQL query from a filter.\n * 1. Creates the matchers, for example 'resource.service.name = \"some_value\"' or 'resource.service.name =~ \"some_value|other_value\"'\n * 2. Join all matchers with '&&'\n * 3. Return the full TraceQL query, for example '{ resource.service.name = \"some_value\" && name = \"span_name\" }'\n */\nexport function filterToTraceQL(filter: Filter) {\n const matchers: string[] = [\n ...stringMatcher('resource.service.name', filter.serviceName),\n ...stringMatcher('name', filter.spanName),\n ...stringMatcher('resource.k8s.namespace.name', filter.namespace),\n ...intrinsicMatcher('status', filter.status),\n ...durationMatcher('duration', filter.spanDuration),\n ...durationMatcher('traceDuration', filter.traceDuration),\n ...customMatcher(filter.customMatchers),\n ];\n\n if (matchers.length === 0) {\n return '{}';\n }\n return `{ ${matchers.join(' && ')} }`;\n}\n\nfunction escape(q: string) {\n return q.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"');\n}\n\nfunction stringMatcher(attribute: string, values: string[]) {\n const escapedValues = values.map(escape);\n if (escapedValues.length > 1) {\n return [`${attribute} =~ \"${escapedValues.join('|')}\"`];\n } else if (escapedValues.length == 1) {\n return [`${attribute} = \"${escapedValues[0]}\"`];\n }\n return [];\n}\n\nfunction intrinsicMatcher(attribute: string, values: string[]) {\n const orConds = values.map((x) => `${attribute} = ${x}`);\n if (orConds.length > 1) {\n return ['(' + orConds.join(' || ') + ')'];\n } else if (orConds.length === 1) {\n return orConds;\n } else {\n return [];\n }\n}\n\nfunction durationMatcher(attribute: string, value: DurationField) {\n const matchers = [];\n if (value.min) {\n matchers.push(`${attribute} >= ${value.min}`);\n }\n if (value.max) {\n matchers.push(`${attribute} <= ${value.max}`);\n }\n return matchers;\n}\n\nfunction customMatcher(customMatchers: string[]) {\n return customMatchers;\n}\n"],"names":["filterToTraceQL","filter","matchers","stringMatcher","serviceName","spanName","namespace","intrinsicMatcher","status","durationMatcher","spanDuration","traceDuration","customMatcher","customMatchers","length","join","escape","q","replaceAll","attribute","values","escapedValues","map","orConds","x","value","min","push","max"],"mappings":"AAEA;;;;;CAKC,GACD,OAAO,SAASA,gBAAgBC,MAAc;IAC5C,MAAMC,WAAqB;WACtBC,cAAc,yBAAyBF,OAAOG,WAAW;WACzDD,cAAc,QAAQF,OAAOI,QAAQ;WACrCF,cAAc,+BAA+BF,OAAOK,SAAS;WAC7DC,iBAAiB,UAAUN,OAAOO,MAAM;WACxCC,gBAAgB,YAAYR,OAAOS,YAAY;WAC/CD,gBAAgB,iBAAiBR,OAAOU,aAAa;WACrDC,cAAcX,OAAOY,cAAc;KACvC;IAED,IAAIX,SAASY,MAAM,KAAK,GAAG;QACzB,OAAO;IACT;IACA,OAAO,CAAC,EAAE,EAAEZ,SAASa,IAAI,CAAC,QAAQ,EAAE,CAAC;AACvC;AAEA,SAASC,OAAOC,CAAS;IACvB,OAAOA,EAAEC,UAAU,CAAC,MAAM,QAAQA,UAAU,CAAC,KAAK;AACpD;AAEA,SAASf,cAAcgB,SAAiB,EAAEC,MAAgB;IACxD,MAAMC,gBAAgBD,OAAOE,GAAG,CAACN;IACjC,IAAIK,cAAcP,MAAM,GAAG,GAAG;QAC5B,OAAO;YAAC,GAAGK,UAAU,KAAK,EAAEE,cAAcN,IAAI,CAAC,KAAK,CAAC,CAAC;SAAC;IACzD,OAAO,IAAIM,cAAcP,MAAM,IAAI,GAAG;QACpC,OAAO;YAAC,GAAGK,UAAU,IAAI,EAAEE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;SAAC;IACjD;IACA,OAAO,EAAE;AACX;AAEA,SAASd,iBAAiBY,SAAiB,EAAEC,MAAgB;IAC3D,MAAMG,UAAUH,OAAOE,GAAG,CAAC,CAACE,IAAM,GAAGL,UAAU,GAAG,EAAEK,GAAG;IACvD,IAAID,QAAQT,MAAM,GAAG,GAAG;QACtB,OAAO;YAAC,MAAMS,QAAQR,IAAI,CAAC,UAAU;SAAI;IAC3C,OAAO,IAAIQ,QAAQT,MAAM,KAAK,GAAG;QAC/B,OAAOS;IACT,OAAO;QACL,OAAO,EAAE;IACX;AACF;AAEA,SAASd,gBAAgBU,SAAiB,EAAEM,KAAoB;IAC9D,MAAMvB,WAAW,EAAE;IACnB,IAAIuB,MAAMC,GAAG,EAAE;QACbxB,SAASyB,IAAI,CAAC,GAAGR,UAAU,IAAI,EAAEM,MAAMC,GAAG,EAAE;IAC9C;IACA,IAAID,MAAMG,GAAG,EAAE;QACb1B,SAASyB,IAAI,CAAC,GAAGR,UAAU,IAAI,EAAEM,MAAMG,GAAG,EAAE;IAC9C;IACA,OAAO1B;AACT;AAEA,SAASU,cAAcC,cAAwB;IAC7C,OAAOA;AACT"}
@@ -0,0 +1,4 @@
1
+ export * from './filter';
2
+ export * from './filter_to_traceql';
3
+ export * from './traceql_to_filter';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/filter/index.ts"],"names":[],"mappings":"AAaA,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,17 @@
1
+ // Copyright 2025 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ export * from './filter';
14
+ export * from './filter_to_traceql';
15
+ export * from './traceql_to_filter';
16
+
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/filter/index.ts"],"sourcesContent":["// Copyright 2025 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nexport * from './filter';\nexport * from './filter_to_traceql';\nexport * from './traceql_to_filter';\n"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAEjC,cAAc,WAAW;AACzB,cAAc,sBAAsB;AACpC,cAAc,sBAAsB"}
@@ -0,0 +1,9 @@
1
+ import { Filter } from './filter';
2
+ /**
3
+ * Construct a Filter from a TraceQL query.
4
+ * 1. Parse the query (using Lezer library) and extract all matchers, e.g. 'some_attribute = "some_value"'
5
+ * 2. Create the filter attribute values, a string array with a single value (for 'x = "y"') or multiple values (for 'x =~ "y|z"')
6
+ * 3. Add the remaining matchers to the set of custom matchers.
7
+ */
8
+ export declare function traceQLToFilter(query: string): Filter;
9
+ //# sourceMappingURL=traceql_to_filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceql_to_filter.d.ts","sourceRoot":"","sources":["../../../../src/components/filter/traceql_to_filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,MAAM,EAAE,MAAM,UAAU,CAAC;AAOjD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAcrD"}
@@ -0,0 +1,110 @@
1
+ import { AttributeField, FieldExpression, FieldOp, IntrinsicField, Static, parser } from '@grafana/lezer-traceql';
2
+ /**
3
+ * Construct a Filter from a TraceQL query.
4
+ * 1. Parse the query (using Lezer library) and extract all matchers, e.g. 'some_attribute = "some_value"'
5
+ * 2. Create the filter attribute values, a string array with a single value (for 'x = "y"') or multiple values (for 'x =~ "y|z"')
6
+ * 3. Add the remaining matchers to the set of custom matchers.
7
+ */ export function traceQLToFilter(query) {
8
+ const matchers = parseQuery(query);
9
+ return {
10
+ serviceName: reverseStringMatcher(matchers['resource.service.name']),
11
+ spanName: reverseStringMatcher(matchers['name']),
12
+ namespace: reverseStringMatcher(matchers['resource.k8s.namespace.name']),
13
+ status: reverseIntrinsicMatcher(matchers['status']),
14
+ spanDuration: reverseDurationMatcher(matchers['duration']),
15
+ traceDuration: reverseDurationMatcher(matchers['traceDuration']),
16
+ customMatchers: reverseCustomMatcher(matchers, new Set([
17
+ 'resource.service.name',
18
+ 'name',
19
+ 'resource.k8s.namespace.name',
20
+ 'status',
21
+ 'duration',
22
+ 'traceDuration'
23
+ ]))
24
+ };
25
+ }
26
+ function parseQuery(query) {
27
+ const matchers = {};
28
+ let attribute = '';
29
+ let operator = '';
30
+ let value = '';
31
+ const syntaxTree = parser.parse(query);
32
+ syntaxTree.iterate({
33
+ enter (node) {
34
+ switch(node.type.id){
35
+ case AttributeField:
36
+ attribute = query.slice(node.from, node.to);
37
+ return false;
38
+ case IntrinsicField:
39
+ attribute = query.slice(node.from, node.to);
40
+ return false;
41
+ case FieldOp:
42
+ operator = query.slice(node.from, node.to);
43
+ return false;
44
+ case Static:
45
+ value = query.slice(node.from, node.to);
46
+ return false;
47
+ }
48
+ },
49
+ leave (node) {
50
+ if (node.type.id === FieldExpression && node.node.getChild(FieldOp)) {
51
+ const newMatchers = matchers[attribute] ?? [];
52
+ newMatchers.push({
53
+ operator,
54
+ value
55
+ });
56
+ matchers[attribute] = newMatchers;
57
+ }
58
+ }
59
+ });
60
+ return matchers;
61
+ }
62
+ function unescape(q) {
63
+ return q.replaceAll('\\"', '"').replaceAll('\\\\', '\\');
64
+ }
65
+ function reverseStringMatcher(matches) {
66
+ const values = [];
67
+ for (const { operator, value } of matches ?? []){
68
+ const unescaped = unescape(value.slice(1, -1));
69
+ if (operator == '=') {
70
+ values.push(unescaped);
71
+ } else if (operator == '=~') {
72
+ values.push(...unescaped.split('|'));
73
+ }
74
+ }
75
+ return values;
76
+ }
77
+ function reverseIntrinsicMatcher(matches) {
78
+ const values = [];
79
+ for (const { operator, value } of matches ?? []){
80
+ if (operator == '=') {
81
+ values.push(value);
82
+ }
83
+ }
84
+ return values;
85
+ }
86
+ function reverseDurationMatcher(matches) {
87
+ const duration = {};
88
+ for (const { operator, value } of matches ?? []){
89
+ if (operator == '>=') {
90
+ duration.min = value;
91
+ } else if (operator == '<=') {
92
+ duration.max = value;
93
+ }
94
+ }
95
+ return duration;
96
+ }
97
+ function reverseCustomMatcher(matchers, skipAttrs) {
98
+ const customMatchers = [];
99
+ for (const [attribute, matches] of Object.entries(matchers)){
100
+ if (skipAttrs.has(attribute)) {
101
+ continue;
102
+ }
103
+ for (const { operator, value } of matches){
104
+ customMatchers.push(`${attribute}${operator}${value}`);
105
+ }
106
+ }
107
+ return customMatchers;
108
+ }
109
+
110
+ //# sourceMappingURL=traceql_to_filter.js.map