@perses-dev/loki-plugin 0.4.0 → 0.5.0-beta.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 (155) hide show
  1. package/__mf/js/Loki.b64c7408.js +6 -0
  2. package/__mf/js/async/1238.71fd7843.js +1 -0
  3. package/__mf/js/async/1490.0d8dcc42.js +22 -0
  4. package/__mf/js/async/1501.eefd56b9.js +1 -0
  5. package/__mf/js/async/1616.60c9a082.js +1 -0
  6. package/__mf/js/async/1728.21a8f690.js +1 -0
  7. package/__mf/js/async/1811.a291a38b.js +28 -0
  8. package/__mf/js/async/1825.5005b3af.js +1 -0
  9. package/__mf/js/async/1969.53d223ca.js +1 -0
  10. package/__mf/js/async/2043.eb7e1c61.js +2 -0
  11. package/__mf/js/async/2386.a3cfef97.js +2 -0
  12. package/__mf/js/async/3059.cb101ca2.js +1 -0
  13. package/__mf/js/async/3181.d2b90b95.js +2 -0
  14. package/__mf/js/async/3849.85df5535.js +7 -0
  15. package/__mf/js/async/392.c0f9d49a.js +2 -0
  16. package/__mf/js/async/{1964.7b2e7223.js → 4121.328a1e93.js} +2 -2
  17. package/__mf/js/async/4466.21ddc88c.js +74 -0
  18. package/__mf/js/async/{2651.ef335482.js → 5002.29685f8e.js} +1 -1
  19. package/__mf/js/async/5071.a03a64fd.js +1 -0
  20. package/__mf/js/async/5440.3fedd5ea.js +2 -0
  21. package/__mf/js/async/5501.01af8b43.js +2 -0
  22. package/__mf/js/async/{5214.fee648bb.js → 5587.55dbc663.js} +1 -1
  23. package/__mf/js/async/6100.e2898f9c.js +1 -0
  24. package/__mf/js/async/6274.293b47c4.js +2 -0
  25. package/__mf/js/async/6283.78af4bc8.js +2 -0
  26. package/__mf/js/async/6498.903f2a94.js +2 -0
  27. package/__mf/js/async/7177.b50a1c64.js +1 -0
  28. package/__mf/js/async/7192.4dea1fd0.js +1 -0
  29. package/__mf/js/async/7370.0ddd349a.js +2 -0
  30. package/__mf/js/async/7968.ec040694.js +1 -0
  31. package/__mf/js/async/7978.38078276.js +1 -0
  32. package/__mf/js/async/8035.8dab4893.js +38 -0
  33. package/__mf/js/async/8356.5635696c.js +1 -0
  34. package/__mf/js/async/8470.c67049a2.js +2 -0
  35. package/__mf/js/async/8482.07caae1a.js +1 -0
  36. package/__mf/js/async/873.02aa55af.js +1 -0
  37. package/__mf/js/async/8988.1c565f12.js +1 -0
  38. package/__mf/js/async/9071.ed72bdac.js +2 -0
  39. package/__mf/js/async/9235.f2e0e95c.js +1 -0
  40. package/__mf/js/async/9389.a8ea42a0.js +2 -0
  41. package/__mf/js/async/941.0bce16fe.js +2 -0
  42. package/__mf/js/async/9588.2d82f477.js +1 -0
  43. package/__mf/js/async/9754.5d7b21c2.js +10 -0
  44. package/__mf/js/async/9836.de786d07.js +1 -0
  45. package/__mf/js/async/__federation_expose_LokiDatasource.3da46ee2.js +2 -0
  46. package/__mf/js/async/__federation_expose_LokiLogQuery.c1be0273.js +1 -0
  47. package/__mf/js/async/__federation_expose_LokiTimeSeriesQuery.cf876f4b.js +1 -0
  48. package/__mf/js/main.de30f7e6.js +6 -0
  49. package/lib/bootstrap.js +3 -3
  50. package/lib/bootstrap.js.map +1 -1
  51. package/lib/cjs/bootstrap.js +1 -1
  52. package/lib/cjs/components/complete.js +258 -0
  53. package/lib/cjs/components/logql-editor.js +6 -3
  54. package/lib/cjs/components/logql-extension.js +15 -1
  55. package/lib/cjs/model/index.js +1 -0
  56. package/lib/cjs/model/loki-client.js +10 -10
  57. package/lib/cjs/model/loki-selectors.js +5 -5
  58. package/lib/cjs/queries/constants.js +3 -3
  59. package/lib/cjs/queries/loki-log-query/LokiLogQueryEditor.js +16 -1
  60. package/lib/components/complete.d.ts +37 -0
  61. package/lib/components/complete.d.ts.map +1 -0
  62. package/lib/components/complete.js +249 -0
  63. package/lib/components/complete.js.map +1 -0
  64. package/lib/components/logql-editor.d.ts +4 -1
  65. package/lib/components/logql-editor.d.ts.map +1 -1
  66. package/lib/components/logql-editor.js +7 -4
  67. package/lib/components/logql-editor.js.map +1 -1
  68. package/lib/components/logql-extension.d.ts +9 -1
  69. package/lib/components/logql-extension.d.ts.map +1 -1
  70. package/lib/components/logql-extension.js +15 -1
  71. package/lib/components/logql-extension.js.map +1 -1
  72. package/lib/datasources/loki-datasource/LokiDatasourceEditor.js +1 -1
  73. package/lib/datasources/loki-datasource/LokiDatasourceEditor.js.map +1 -1
  74. package/lib/model/index.d.ts +1 -0
  75. package/lib/model/index.d.ts.map +1 -1
  76. package/lib/model/index.js +1 -0
  77. package/lib/model/index.js.map +1 -1
  78. package/lib/queries/loki-log-query/LokiLogQueryEditor.d.ts.map +1 -1
  79. package/lib/queries/loki-log-query/LokiLogQueryEditor.js +19 -4
  80. package/lib/queries/loki-log-query/LokiLogQueryEditor.js.map +1 -1
  81. package/lib/queries/loki-log-query/get-loki-log-data.js.map +1 -1
  82. package/lib/queries/loki-time-series-query/LokiTimeSeriesQueryEditor.js +1 -1
  83. package/lib/queries/loki-time-series-query/LokiTimeSeriesQueryEditor.js.map +1 -1
  84. package/lib/queries/loki-time-series-query/get-loki-time-series-data.d.ts +1 -4
  85. package/lib/queries/loki-time-series-query/get-loki-time-series-data.d.ts.map +1 -1
  86. package/lib/queries/loki-time-series-query/get-loki-time-series-data.js.map +1 -1
  87. package/mf-manifest.json +107 -102
  88. package/mf-stats.json +107 -102
  89. package/package.json +7 -6
  90. package/__mf/js/Loki.c48c56a7.js +0 -5
  91. package/__mf/js/async/1360.2348e2a7.js +0 -10
  92. package/__mf/js/async/1398.ca579c40.js +0 -2
  93. package/__mf/js/async/1540.6b797827.js +0 -74
  94. package/__mf/js/async/1580.c7d3c3f1.js +0 -2
  95. package/__mf/js/async/2226.a056d1a3.js +0 -1
  96. package/__mf/js/async/2292.35c2eeec.js +0 -2
  97. package/__mf/js/async/2652.7b7038d2.js +0 -28
  98. package/__mf/js/async/282.6c4c5a94.js +0 -1
  99. package/__mf/js/async/3224.b1170acc.js +0 -1
  100. package/__mf/js/async/3431.2270637c.js +0 -1
  101. package/__mf/js/async/3863.16343b76.js +0 -2
  102. package/__mf/js/async/3960.6091bb00.js +0 -2
  103. package/__mf/js/async/3980.08ab4aef.js +0 -2
  104. package/__mf/js/async/4075.19dee570.js +0 -1
  105. package/__mf/js/async/4238.155e3b8a.js +0 -1
  106. package/__mf/js/async/4269.b4d0f49d.js +0 -2
  107. package/__mf/js/async/4310.6d2d2ce3.js +0 -7
  108. package/__mf/js/async/4421.28cc8e2d.js +0 -1
  109. package/__mf/js/async/4557.fd670526.js +0 -2
  110. package/__mf/js/async/4676.d4c41b7a.js +0 -22
  111. package/__mf/js/async/5409.ff95cf63.js +0 -1
  112. package/__mf/js/async/5780.d837c3cd.js +0 -1
  113. package/__mf/js/async/5981.a46c5157.js +0 -2
  114. package/__mf/js/async/6134.f7ee513c.js +0 -38
  115. package/__mf/js/async/6292.9997ca36.js +0 -1
  116. package/__mf/js/async/6329.9c3a3698.js +0 -2
  117. package/__mf/js/async/6333.01fb6457.js +0 -2
  118. package/__mf/js/async/6377.9f308c7f.js +0 -2
  119. package/__mf/js/async/6770.ba5a38f3.js +0 -1
  120. package/__mf/js/async/694.c25a1f84.js +0 -1
  121. package/__mf/js/async/7376.0459aaf7.js +0 -1
  122. package/__mf/js/async/738.a16c93b6.js +0 -1
  123. package/__mf/js/async/7740.6f193fac.js +0 -1
  124. package/__mf/js/async/7797.822237c9.js +0 -1
  125. package/__mf/js/async/8216.d9cc6234.js +0 -1
  126. package/__mf/js/async/8488.5b4b7170.js +0 -1
  127. package/__mf/js/async/8537.dc791586.js +0 -1
  128. package/__mf/js/async/9173.75bbe78a.js +0 -2
  129. package/__mf/js/async/9554.728cf7b9.js +0 -2
  130. package/__mf/js/async/__federation_expose_LokiDatasource.3ce6abca.js +0 -2
  131. package/__mf/js/async/__federation_expose_LokiLogQuery.c446c7ff.js +0 -1
  132. package/__mf/js/async/__federation_expose_LokiTimeSeriesQuery.eda99bf8.js +0 -1
  133. package/__mf/js/main.ccf461ea.js +0 -5
  134. /package/__mf/css/async/{1580.d3010b86.css → 3061.d3010b86.css} +0 -0
  135. /package/__mf/css/async/{2341.d3010b86.css → 5442.d3010b86.css} +0 -0
  136. /package/__mf/css/async/{6759.d3010b86.css → 7370.d3010b86.css} +0 -0
  137. /package/__mf/js/async/{4676.d4c41b7a.js.LICENSE.txt → 1490.0d8dcc42.js.LICENSE.txt} +0 -0
  138. /package/__mf/js/async/{2292.35c2eeec.js.LICENSE.txt → 2043.eb7e1c61.js.LICENSE.txt} +0 -0
  139. /package/__mf/js/async/{1398.ca579c40.js.LICENSE.txt → 2386.a3cfef97.js.LICENSE.txt} +0 -0
  140. /package/__mf/js/async/{1580.c7d3c3f1.js.LICENSE.txt → 3181.d2b90b95.js.LICENSE.txt} +0 -0
  141. /package/__mf/js/async/{4310.6d2d2ce3.js.LICENSE.txt → 3849.85df5535.js.LICENSE.txt} +0 -0
  142. /package/__mf/js/async/{5981.a46c5157.js.LICENSE.txt → 392.c0f9d49a.js.LICENSE.txt} +0 -0
  143. /package/__mf/js/async/{1964.7b2e7223.js.LICENSE.txt → 4121.328a1e93.js.LICENSE.txt} +0 -0
  144. /package/__mf/js/async/{6377.9f308c7f.js.LICENSE.txt → 5440.3fedd5ea.js.LICENSE.txt} +0 -0
  145. /package/__mf/js/async/{4557.fd670526.js.LICENSE.txt → 5501.01af8b43.js.LICENSE.txt} +0 -0
  146. /package/__mf/js/async/{3960.6091bb00.js.LICENSE.txt → 6274.293b47c4.js.LICENSE.txt} +0 -0
  147. /package/__mf/js/async/{3980.08ab4aef.js.LICENSE.txt → 6283.78af4bc8.js.LICENSE.txt} +0 -0
  148. /package/__mf/js/async/{9554.728cf7b9.js.LICENSE.txt → 6498.903f2a94.js.LICENSE.txt} +0 -0
  149. /package/__mf/js/async/{3863.16343b76.js.LICENSE.txt → 7370.0ddd349a.js.LICENSE.txt} +0 -0
  150. /package/__mf/js/async/{9173.75bbe78a.js.LICENSE.txt → 8470.c67049a2.js.LICENSE.txt} +0 -0
  151. /package/__mf/js/async/{4269.b4d0f49d.js.LICENSE.txt → 9071.ed72bdac.js.LICENSE.txt} +0 -0
  152. /package/__mf/js/async/{6329.9c3a3698.js.LICENSE.txt → 9389.a8ea42a0.js.LICENSE.txt} +0 -0
  153. /package/__mf/js/async/{6333.01fb6457.js.LICENSE.txt → 941.0bce16fe.js.LICENSE.txt} +0 -0
  154. /package/__mf/js/async/{1360.2348e2a7.js.LICENSE.txt → 9754.5d7b21c2.js.LICENSE.txt} +0 -0
  155. /package/__mf/js/async/{__federation_expose_LokiDatasource.3ce6abca.js.LICENSE.txt → __federation_expose_LokiDatasource.3da46ee2.js.LICENSE.txt} +0 -0
@@ -17,35 +17,35 @@ Object.defineProperty(exports, "__esModule", {
17
17
  function _export(target, all) {
18
18
  for(var name in all)Object.defineProperty(target, name, {
19
19
  enumerable: true,
20
- get: all[name]
20
+ get: Object.getOwnPropertyDescriptor(all, name).get
21
21
  });
22
22
  }
23
23
  _export(exports, {
24
- indexStats: function() {
24
+ get indexStats () {
25
25
  return indexStats;
26
26
  },
27
- labelValues: function() {
27
+ get labelValues () {
28
28
  return labelValues;
29
29
  },
30
- labels: function() {
30
+ get labels () {
31
31
  return labels;
32
32
  },
33
- query: function() {
33
+ get query () {
34
34
  return query;
35
35
  },
36
- queryRange: function() {
36
+ get queryRange () {
37
37
  return queryRange;
38
38
  },
39
- series: function() {
39
+ get series () {
40
40
  return series;
41
41
  },
42
- toUnixSeconds: function() {
42
+ get toUnixSeconds () {
43
43
  return toUnixSeconds;
44
44
  },
45
- volume: function() {
45
+ get volume () {
46
46
  return volume;
47
47
  },
48
- volumeRange: function() {
48
+ get volumeRange () {
49
49
  return volumeRange;
50
50
  }
51
51
  });
@@ -17,20 +17,20 @@ Object.defineProperty(exports, "__esModule", {
17
17
  function _export(target, all) {
18
18
  for(var name in all)Object.defineProperty(target, name, {
19
19
  enumerable: true,
20
- get: all[name]
20
+ get: Object.getOwnPropertyDescriptor(all, name).get
21
21
  });
22
22
  }
23
23
  _export(exports, {
24
- DEFAULT_LOKI: function() {
24
+ get DEFAULT_LOKI () {
25
25
  return DEFAULT_LOKI;
26
26
  },
27
- LOKI_DATASOURCE_KIND: function() {
27
+ get LOKI_DATASOURCE_KIND () {
28
28
  return LOKI_DATASOURCE_KIND;
29
29
  },
30
- isDefaultLokiSelector: function() {
30
+ get isDefaultLokiSelector () {
31
31
  return isDefaultLokiSelector;
32
32
  },
33
- isLokiDatasourceSelector: function() {
33
+ get isLokiDatasourceSelector () {
34
34
  return isLokiDatasourceSelector;
35
35
  }
36
36
  });
@@ -5,14 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  function _export(target, all) {
6
6
  for(var name in all)Object.defineProperty(target, name, {
7
7
  enumerable: true,
8
- get: all[name]
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
- DATASOURCE_KIND: function() {
12
+ get DATASOURCE_KIND () {
13
13
  return DATASOURCE_KIND;
14
14
  },
15
- DEFAULT_DATASOURCE: function() {
15
+ get DEFAULT_DATASOURCE () {
16
16
  return DEFAULT_DATASOURCE;
17
17
  }
18
18
  });
@@ -36,6 +36,20 @@ function LokiLogQueryEditor(props) {
36
36
  const datasourceSelectValue = datasource ?? _constants.DEFAULT_DATASOURCE;
37
37
  const selectedDatasource = (0, _pluginsystem.useDatasourceSelectValueToSelector)(datasourceSelectValue, _model.LOKI_DATASOURCE_KIND);
38
38
  const { query, handleQueryChange, handleQueryBlur } = (0, _queryeditormodel.useQueryState)(props);
39
+ // Get client and time range for autocompletion
40
+ const { data: client } = (0, _pluginsystem.useDatasourceClient)(selectedDatasource);
41
+ const { absoluteTimeRange } = (0, _pluginsystem.useTimeRange)();
42
+ // Create completion config for autocompletion
43
+ const completionConfig = (0, _react.useMemo)(()=>{
44
+ if (!client) return undefined;
45
+ return {
46
+ client,
47
+ timeRange: absoluteTimeRange
48
+ };
49
+ }, [
50
+ client,
51
+ absoluteTimeRange
52
+ ]);
39
53
  const handleDatasourceChange = (newDatasourceSelection)=>{
40
54
  if (!(0, _pluginsystem.isVariableDatasource)(newDatasourceSelection) && newDatasourceSelection.kind === _constants.DATASOURCE_KIND) {
41
55
  onChange((0, _immer.produce)(value, (draft)=>{
@@ -104,7 +118,8 @@ function LokiLogQueryEditor(props) {
104
118
  handleQueryExecute(query);
105
119
  }
106
120
  },
107
- placeholder: 'Enter LogQL query (e.g. {job="mysql"} |= "error")'
121
+ placeholder: 'Enter LogQL query (e.g. {job="mysql"} |= "error")',
122
+ completionConfig: completionConfig
108
123
  })
109
124
  ]
110
125
  }),
@@ -0,0 +1,37 @@
1
+ import { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
2
+ import { EditorState } from '@codemirror/state';
3
+ import { Tree } from '@lezer/common';
4
+ import { EditorView } from '@uiw/react-codemirror';
5
+ import { CompletionConfig } from './logql-extension';
6
+ /** CompletionScope specifies the completion kind, e.g. whether to complete label names or values */
7
+ type CompletionScope = {
8
+ kind: 'LabelName';
9
+ } | {
10
+ kind: 'LabelValue';
11
+ label: string;
12
+ };
13
+ /**
14
+ * CompletionInfo specifies the identified scope and position of the completion in the current editor text.
15
+ */
16
+ export interface CompletionInfo {
17
+ scope: CompletionScope;
18
+ from: number;
19
+ to?: number;
20
+ }
21
+ export declare function complete(completionCfg: CompletionConfig, { state, pos }: CompletionContext): Promise<CompletionResult | null>;
22
+ /**
23
+ * Identify completion scope (e.g. LabelName, LabelValue) and position, based on the current node in the syntax tree.
24
+ *
25
+ * Function is exported for tests only.
26
+ */
27
+ export declare function identifyCompletion(state: EditorState, pos: number, tree: Tree): CompletionInfo | undefined;
28
+ /**
29
+ * Add quotes to the completion text in case quotes are not present already.
30
+ * This handles the following cases:
31
+ * { name=HTTP
32
+ * { name="x
33
+ * { name="x" where cursor is after the 'x'
34
+ */
35
+ export declare function applyQuotedCompletion(view: EditorView, completion: Completion, from: number, to: number): void;
36
+ export {};
37
+ //# sourceMappingURL=complete.d.ts.map
@@ -0,0 +1 @@
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;AAErC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,oGAAoG;AACpG,KAAK,eAAe,GAAG;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,eAAe,CAAC;IACvB,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;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,cAAc,GAAG,SAAS,CA6G1G;AAkDD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAmB9G"}
@@ -0,0 +1,249 @@
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
+ import { insertCompletionText } from '@codemirror/autocomplete';
14
+ import { syntaxTree } from '@codemirror/language';
15
+ import { Selector, Matchers, Matcher, Identifier, Eq, Neq, Re, Nre, String as StringType } from '@grafana/lezer-logql';
16
+ import { toUnixSeconds } from '../model/loki-client';
17
+ const quoteChars = [
18
+ '"',
19
+ '`'
20
+ ];
21
+ const defaultQuoteChar = '"';
22
+ export async function complete(completionCfg, { state, pos }) {
23
+ // First, identify the completion scope
24
+ const completion = identifyCompletion(state, pos, syntaxTree(state));
25
+ if (!completion) {
26
+ // No completion scope found for current cursor position.
27
+ return null;
28
+ }
29
+ // Then, retrieve completion options for the identified scope (from the Loki API).
30
+ const options = await retrieveOptions(completionCfg, completion.scope);
31
+ return {
32
+ options,
33
+ from: completion.from,
34
+ to: completion.to
35
+ };
36
+ }
37
+ /**
38
+ * Identify completion scope (e.g. LabelName, LabelValue) and position, based on the current node in the syntax tree.
39
+ *
40
+ * Function is exported for tests only.
41
+ */ export function identifyCompletion(state, pos, tree) {
42
+ const node = tree.resolveInner(pos, -1);
43
+ switch(node.type.id){
44
+ case Selector:
45
+ // Selector is the entire {label matchers} expression
46
+ // Autocomplete at start: {▯ or empty: {}
47
+ // Do not autocomplete if cursor is after closing brace: {job="mysql"}▯
48
+ if ((node.firstChild === null || node.firstChild?.type.id === 0) && !state.sliceDoc(node.from, pos).includes('}')) {
49
+ return {
50
+ scope: {
51
+ kind: 'LabelName'
52
+ },
53
+ from: pos
54
+ };
55
+ }
56
+ break;
57
+ case Matchers:
58
+ {
59
+ // Matchers node contains all label matchers inside {}
60
+ // Autocomplete after comma: { job="mysql",▯ or { job="mysql", ▯
61
+ const text = state.sliceDoc(node.from, pos);
62
+ if (text.endsWith(',') || text.endsWith(', ')) {
63
+ return {
64
+ scope: {
65
+ kind: 'LabelName'
66
+ },
67
+ from: pos
68
+ };
69
+ }
70
+ break;
71
+ }
72
+ case Matcher:
73
+ // Single matcher like job="mysql"
74
+ // Autocomplete when cursor is after a complete matcher: { job="mysql" ▯
75
+ if (node.parent?.type.id === Matchers) {
76
+ return {
77
+ scope: {
78
+ kind: 'LabelName'
79
+ },
80
+ from: pos
81
+ };
82
+ }
83
+ break;
84
+ case Identifier:
85
+ // Identifier is a label name being typed
86
+ // Autocomplete partial label names: { jo▯ or { job="mysql", na▯
87
+ if (node.parent?.type.id === Matcher) {
88
+ return {
89
+ scope: {
90
+ kind: 'LabelName'
91
+ },
92
+ from: node.from
93
+ };
94
+ }
95
+ break;
96
+ case Eq:
97
+ case Neq:
98
+ case Re:
99
+ case Nre:
100
+ // Operators for label matching: =, !=, =~, !~
101
+ // Autocomplete label values right after operator: { job=▯ or { job!=▯
102
+ if (node.parent?.type.id === Matcher) {
103
+ const labelNode = node.parent.firstChild;
104
+ if (labelNode?.type.id === Identifier) {
105
+ const label = state.sliceDoc(labelNode.from, labelNode.to);
106
+ return {
107
+ scope: {
108
+ kind: 'LabelValue',
109
+ label
110
+ },
111
+ from: pos
112
+ };
113
+ }
114
+ }
115
+ break;
116
+ case StringType:
117
+ // String value in a matcher: { job="▯
118
+ // Do not autocomplete if cursor is after closing quotes: { job=""▯
119
+ if (node.parent?.type.id === Matcher && !/^".*"$/.test(state.sliceDoc(node.from, pos))) {
120
+ const labelNode = node.parent.firstChild;
121
+ if (labelNode?.type.id === Identifier) {
122
+ const label = state.sliceDoc(labelNode.from, labelNode.to);
123
+ return {
124
+ scope: {
125
+ kind: 'LabelValue',
126
+ label
127
+ },
128
+ from: node.from + 1
129
+ }; // +1 to skip opening quote
130
+ }
131
+ }
132
+ break;
133
+ case 0 /* error node */ :
134
+ // Error nodes represent incomplete or malformed syntax
135
+ // Autocomplete incomplete value after operator: { job=mys▯ or { job="mys▯
136
+ if ((node.prevSibling?.type.id === Eq || node.prevSibling?.type.id === Neq || node.prevSibling?.type.id === Re || node.prevSibling?.type.id === Nre) && node.parent?.type.id === Matcher) {
137
+ const labelNode = node.parent.firstChild;
138
+ if (labelNode?.type.id === Identifier) {
139
+ const label = state.sliceDoc(labelNode.from, labelNode.to);
140
+ // Skip leading quote if present: { name="HT▯
141
+ const from = quoteChars.includes(state.sliceDoc(node.from, node.from + 1)) ? node.from + 1 : node.from;
142
+ return {
143
+ scope: {
144
+ kind: 'LabelValue',
145
+ label
146
+ },
147
+ from
148
+ };
149
+ }
150
+ }
151
+ // Autocomplete partial label name: { j▯ or { job="mysql", n▯
152
+ if (node.parent?.type.id === Selector || node.parent?.type.id === Matchers) {
153
+ return {
154
+ scope: {
155
+ kind: 'LabelName'
156
+ },
157
+ from: node.from
158
+ };
159
+ }
160
+ break;
161
+ }
162
+ }
163
+ /**
164
+ * Retrieve completion options based on the identified completion scope.
165
+ */ async function retrieveOptions(completionCfg, completion) {
166
+ switch(completion.kind){
167
+ case 'LabelName':
168
+ return completeLabelName(completionCfg);
169
+ case 'LabelValue':
170
+ return completeLabelValue(completionCfg, completion.label);
171
+ }
172
+ }
173
+ async function completeLabelName(completionCfg) {
174
+ if (!completionCfg.client) {
175
+ return [];
176
+ }
177
+ const start = completionCfg.timeRange?.start ? toUnixSeconds(new Date(completionCfg.timeRange.start).getTime()) : undefined;
178
+ const end = completionCfg.timeRange?.end ? toUnixSeconds(new Date(completionCfg.timeRange.end).getTime()) : undefined;
179
+ try {
180
+ const response = await completionCfg.client.labels(start, end);
181
+ if (response.status === 'success') {
182
+ return response.data.map((label)=>({
183
+ label
184
+ }));
185
+ }
186
+ return [];
187
+ } catch (error) {
188
+ console.error('Error fetching label names:', error);
189
+ return [];
190
+ }
191
+ }
192
+ function escapeString(input, quoteChar) {
193
+ // do not escape raw strings (when using backticks)
194
+ if (quoteChar === '`') {
195
+ return input;
196
+ }
197
+ let escaped = input;
198
+ // escape backslashes and quotes
199
+ escaped = escaped.replaceAll('\\', '\\\\');
200
+ escaped = escaped.replaceAll('"', '\\"');
201
+ return escaped;
202
+ }
203
+ /**
204
+ * Add quotes to the completion text in case quotes are not present already.
205
+ * This handles the following cases:
206
+ * { name=HTTP
207
+ * { name="x
208
+ * { name="x" where cursor is after the 'x'
209
+ */ export function applyQuotedCompletion(view, completion, from, to) {
210
+ let quoteChar = defaultQuoteChar;
211
+ if (quoteChars.includes(view.state.sliceDoc(from - 1, from))) {
212
+ quoteChar = view.state.sliceDoc(from - 1, from);
213
+ from--;
214
+ }
215
+ if (quoteChars.includes(view.state.sliceDoc(to, to + 1))) {
216
+ quoteChar = view.state.sliceDoc(to, to + 1);
217
+ to++;
218
+ }
219
+ // When using raw strings (`), we cannot escape a backtick.
220
+ // Therefore, switch the quote character.
221
+ if (completion.label.includes('`')) {
222
+ quoteChar = '"';
223
+ }
224
+ const insertText = `${quoteChar}${escapeString(completion.label, quoteChar)}${quoteChar}`;
225
+ view.dispatch(insertCompletionText(view.state, insertText, from, to));
226
+ }
227
+ async function completeLabelValue(completionCfg, label) {
228
+ if (!completionCfg.client) {
229
+ return [];
230
+ }
231
+ const start = completionCfg.timeRange?.start ? toUnixSeconds(new Date(completionCfg.timeRange.start).getTime()) : undefined;
232
+ const end = completionCfg.timeRange?.end ? toUnixSeconds(new Date(completionCfg.timeRange.end).getTime()) : undefined;
233
+ try {
234
+ const response = await completionCfg.client.labelValues(label, start, end);
235
+ if (response.status === 'success') {
236
+ return response.data.map((value)=>({
237
+ label: value ?? '',
238
+ displayLabel: value ?? '(empty string)',
239
+ apply: applyQuotedCompletion
240
+ }));
241
+ }
242
+ return [];
243
+ } catch (error) {
244
+ console.error(`Error fetching values for label ${label}:`, error);
245
+ return [];
246
+ }
247
+ }
248
+
249
+ //# sourceMappingURL=complete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/complete.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\nimport { Completion, CompletionContext, CompletionResult, insertCompletionText } from '@codemirror/autocomplete';\nimport { syntaxTree } from '@codemirror/language';\nimport { EditorState } from '@codemirror/state';\nimport { Tree } from '@lezer/common';\nimport { Selector, Matchers, Matcher, Identifier, Eq, Neq, Re, Nre, String as StringType } from '@grafana/lezer-logql';\nimport { EditorView } from '@uiw/react-codemirror';\nimport { toUnixSeconds } from '../model/loki-client';\nimport { CompletionConfig } from './logql-extension';\n\n/** CompletionScope specifies the completion kind, e.g. whether to complete label names or values */\ntype CompletionScope = { kind: 'LabelName' } | { kind: 'LabelValue'; label: string };\n\n/**\n * CompletionInfo specifies the identified scope and position of the completion in the current editor text.\n */\nexport interface CompletionInfo {\n scope: 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 scope\n const completion = identifyCompletion(state, pos, syntaxTree(state));\n if (!completion) {\n // No completion scope found for current cursor position.\n return null;\n }\n\n // Then, retrieve completion options for the identified scope (from the Loki API).\n const options = await retrieveOptions(completionCfg, completion.scope);\n return { options, from: completion.from, to: completion.to };\n}\n\n/**\n * Identify completion scope (e.g. LabelName, LabelValue) and position, based on the current node in the syntax tree.\n *\n * Function is exported for tests only.\n */\nexport function identifyCompletion(state: EditorState, pos: number, tree: Tree): CompletionInfo | undefined {\n const node = tree.resolveInner(pos, -1);\n\n switch (node.type.id) {\n case Selector:\n // Selector is the entire {label matchers} expression\n // Autocomplete at start: {▯ or empty: {}\n // Do not autocomplete if cursor is after closing brace: {job=\"mysql\"}▯\n if (\n (node.firstChild === null || node.firstChild?.type.id === 0) &&\n !state.sliceDoc(node.from, pos).includes('}')\n ) {\n return {\n scope: { kind: 'LabelName' },\n from: pos,\n };\n }\n break;\n\n case Matchers: {\n // Matchers node contains all label matchers inside {}\n // Autocomplete after comma: { job=\"mysql\",▯ or { job=\"mysql\", ▯\n const text = state.sliceDoc(node.from, pos);\n if (text.endsWith(',') || text.endsWith(', ')) {\n return {\n scope: { kind: 'LabelName' },\n from: pos,\n };\n }\n break;\n }\n\n case Matcher:\n // Single matcher like job=\"mysql\"\n // Autocomplete when cursor is after a complete matcher: { job=\"mysql\" ▯\n if (node.parent?.type.id === Matchers) {\n return {\n scope: { kind: 'LabelName' },\n from: pos,\n };\n }\n break;\n\n case Identifier:\n // Identifier is a label name being typed\n // Autocomplete partial label names: { jo▯ or { job=\"mysql\", na▯\n if (node.parent?.type.id === Matcher) {\n return {\n scope: { kind: 'LabelName' },\n from: node.from,\n };\n }\n break;\n\n case Eq:\n case Neq:\n case Re:\n case Nre:\n // Operators for label matching: =, !=, =~, !~\n // Autocomplete label values right after operator: { job=▯ or { job!=▯\n if (node.parent?.type.id === Matcher) {\n const labelNode = node.parent.firstChild;\n if (labelNode?.type.id === Identifier) {\n const label = state.sliceDoc(labelNode.from, labelNode.to);\n return { scope: { kind: 'LabelValue', label }, from: pos };\n }\n }\n break;\n\n case StringType:\n // String value in a matcher: { job=\"▯\n // Do not autocomplete if cursor is after closing quotes: { job=\"\"▯\n if (node.parent?.type.id === Matcher && !/^\".*\"$/.test(state.sliceDoc(node.from, pos))) {\n const labelNode = node.parent.firstChild;\n if (labelNode?.type.id === Identifier) {\n const label = state.sliceDoc(labelNode.from, labelNode.to);\n return { scope: { kind: 'LabelValue', label }, from: node.from + 1 }; // +1 to skip opening quote\n }\n }\n break;\n\n case 0 /* error node */:\n // Error nodes represent incomplete or malformed syntax\n // Autocomplete incomplete value after operator: { job=mys▯ or { job=\"mys▯\n if (\n (node.prevSibling?.type.id === Eq ||\n node.prevSibling?.type.id === Neq ||\n node.prevSibling?.type.id === Re ||\n node.prevSibling?.type.id === Nre) &&\n node.parent?.type.id === Matcher\n ) {\n const labelNode = node.parent.firstChild;\n if (labelNode?.type.id === Identifier) {\n const label = state.sliceDoc(labelNode.from, labelNode.to);\n // Skip leading quote if present: { name=\"HT▯\n const from = quoteChars.includes(state.sliceDoc(node.from, node.from + 1)) ? node.from + 1 : node.from;\n return { scope: { kind: 'LabelValue', label }, from };\n }\n }\n\n // Autocomplete partial label name: { j▯ or { job=\"mysql\", n▯\n if (node.parent?.type.id === Selector || node.parent?.type.id === Matchers) {\n return {\n scope: { kind: 'LabelName' },\n from: node.from,\n };\n }\n break;\n }\n}\n\n/**\n * Retrieve completion options based on the identified completion scope.\n */\nasync function retrieveOptions(completionCfg: CompletionConfig, completion: CompletionScope): Promise<Completion[]> {\n switch (completion.kind) {\n case 'LabelName':\n return completeLabelName(completionCfg);\n\n case 'LabelValue':\n return completeLabelValue(completionCfg, completion.label);\n }\n}\n\nasync function completeLabelName(completionCfg: CompletionConfig): Promise<Completion[]> {\n if (!completionCfg.client) {\n return [];\n }\n\n const start = completionCfg.timeRange?.start\n ? toUnixSeconds(new Date(completionCfg.timeRange.start).getTime())\n : undefined;\n const end = completionCfg.timeRange?.end ? toUnixSeconds(new Date(completionCfg.timeRange.end).getTime()) : undefined;\n\n try {\n const response = await completionCfg.client.labels(start, end);\n if (response.status === 'success') {\n return response.data.map((label: string) => ({ label }));\n }\n return [];\n } catch (error) {\n console.error('Error fetching label names:', error);\n return [];\n }\n}\n\nfunction escapeString(input: string, quoteChar: string) {\n // do not escape raw strings (when using backticks)\n if (quoteChar === '`') {\n return input;\n }\n\n let escaped = input;\n // escape backslashes and quotes\n escaped = escaped.replaceAll('\\\\', '\\\\\\\\');\n escaped = escaped.replaceAll('\"', '\\\\\"');\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 from--;\n }\n if (quoteChars.includes(view.state.sliceDoc(to, to + 1))) {\n quoteChar = view.state.sliceDoc(to, to + 1);\n to++;\n }\n\n // When using raw strings (`), we cannot escape a backtick.\n // Therefore, switch the quote character.\n if (completion.label.includes('`')) {\n quoteChar = '\"';\n }\n\n const insertText = `${quoteChar}${escapeString(completion.label, quoteChar)}${quoteChar}`;\n view.dispatch(insertCompletionText(view.state, insertText, from, to));\n}\n\nasync function completeLabelValue(completionCfg: CompletionConfig, label: string): Promise<Completion[]> {\n if (!completionCfg.client) {\n return [];\n }\n\n const start = completionCfg.timeRange?.start\n ? toUnixSeconds(new Date(completionCfg.timeRange.start).getTime())\n : undefined;\n const end = completionCfg.timeRange?.end ? toUnixSeconds(new Date(completionCfg.timeRange.end).getTime()) : undefined;\n\n try {\n const response = await completionCfg.client.labelValues(label, start, end);\n if (response.status === 'success') {\n return response.data.map((value: string) => ({\n label: value ?? '',\n displayLabel: value ?? '(empty string)',\n apply: applyQuotedCompletion,\n }));\n }\n return [];\n } catch (error) {\n console.error(`Error fetching values for label ${label}:`, error);\n return [];\n }\n}\n"],"names":["insertCompletionText","syntaxTree","Selector","Matchers","Matcher","Identifier","Eq","Neq","Re","Nre","String","StringType","toUnixSeconds","quoteChars","defaultQuoteChar","complete","completionCfg","state","pos","completion","identifyCompletion","options","retrieveOptions","scope","from","to","tree","node","resolveInner","type","id","firstChild","sliceDoc","includes","kind","text","endsWith","parent","labelNode","label","test","prevSibling","completeLabelName","completeLabelValue","client","start","timeRange","Date","getTime","undefined","end","response","labels","status","data","map","error","console","escapeString","input","quoteChar","escaped","replaceAll","applyQuotedCompletion","view","insertText","dispatch","labelValues","value","displayLabel","apply"],"mappings":"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"}
@@ -1,5 +1,8 @@
1
1
  import { ReactElement } from 'react';
2
2
  import { ReactCodeMirrorProps } from '@uiw/react-codemirror';
3
- export type LogQLEditorProps = Omit<ReactCodeMirrorProps, 'theme' | 'extensions'>;
3
+ import { CompletionConfig } from './logql-extension';
4
+ export type LogQLEditorProps = Omit<ReactCodeMirrorProps, 'theme' | 'extensions'> & {
5
+ completionConfig?: CompletionConfig;
6
+ };
4
7
  export declare function LogQLEditor(props: LogQLEditorProps): ReactElement;
5
8
  //# sourceMappingURL=logql-editor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logql-editor.d.ts","sourceRoot":"","sources":["../../../src/components/logql-editor.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAE9C,OAAmB,EAAc,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGrF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC;AAElF,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,CAyCjE"}
1
+ {"version":3,"file":"logql-editor.d.ts","sourceRoot":"","sources":["../../../src/components/logql-editor.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAE9C,OAAmB,EAAc,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAkB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErE,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,YAAY,CAAC,GAAG;IAClF,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,CA0CjE"}
@@ -1,3 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  // Copyright 2025 The Perses Authors
2
3
  // Licensed under the Apache License, Version 2.0 (the "License");
3
4
  // you may not use this file except in compliance with the License.
@@ -10,17 +11,19 @@
10
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
12
  // See the License for the specific language governing permissions and
12
13
  // limitations under the License.
13
- import { jsx as _jsx } from "react/jsx-runtime";
14
14
  import { useMemo } from 'react';
15
15
  import { useTheme } from '@mui/material';
16
16
  import CodeMirror, { EditorView } from '@uiw/react-codemirror';
17
17
  import { LogQLExtension } from './logql-extension';
18
18
  export function LogQLEditor(props) {
19
+ const { completionConfig, ...codemirrorProps } = props;
19
20
  const theme = useTheme();
20
21
  const isDarkMode = theme.palette.mode === 'dark';
21
22
  const logqlExtension = useMemo(()=>{
22
- return LogQLExtension();
23
- }, []);
23
+ return LogQLExtension(completionConfig);
24
+ }, [
25
+ completionConfig
26
+ ]);
24
27
  const codemirrorTheme = useMemo(()=>{
25
28
  const borderColor = theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)';
26
29
  return EditorView.theme({
@@ -40,7 +43,7 @@ export function LogQLEditor(props) {
40
43
  theme
41
44
  ]);
42
45
  return /*#__PURE__*/ _jsx(CodeMirror, {
43
- ...props,
46
+ ...codemirrorProps,
44
47
  theme: isDarkMode ? 'dark' : 'light',
45
48
  basicSetup: {
46
49
  lineNumbers: false,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/logql-editor.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 { useTheme } from '@mui/material';\nimport CodeMirror, { EditorView, ReactCodeMirrorProps } from '@uiw/react-codemirror';\nimport { LogQLExtension } from './logql-extension';\n\nexport type LogQLEditorProps = Omit<ReactCodeMirrorProps, 'theme' | 'extensions'>;\n\nexport function LogQLEditor(props: LogQLEditorProps): ReactElement {\n const theme = useTheme();\n const isDarkMode = theme.palette.mode === 'dark';\n\n const logqlExtension = useMemo(() => {\n return LogQLExtension();\n }, []);\n\n const codemirrorTheme = useMemo(() => {\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',\n },\n '.cm-content': {\n padding: '8px',\n },\n });\n }, [theme]);\n\n return (\n <CodeMirror\n {...props}\n theme={isDarkMode ? 'dark' : 'light'}\n basicSetup={{\n lineNumbers: false,\n highlightActiveLine: false,\n highlightActiveLineGutter: false,\n foldGutter: false,\n syntaxHighlighting: true,\n }}\n extensions={[EditorView.lineWrapping, logqlExtension, codemirrorTheme]}\n placeholder='Example: {job=\"my-service\"} |= \"error\"'\n />\n );\n}\n"],"names":["useMemo","useTheme","CodeMirror","EditorView","LogQLExtension","LogQLEditor","props","theme","isDarkMode","palette","mode","logqlExtension","codemirrorTheme","borderColor","backgroundColor","border","borderRadius","shape","outline","padding","basicSetup","lineNumbers","highlightActiveLine","highlightActiveLineGutter","foldGutter","syntaxHighlighting","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,QAAQ,QAAQ,gBAAgB;AACzC,OAAOC,cAAcC,UAAU,QAA8B,wBAAwB;AACrF,SAASC,cAAc,QAAQ,oBAAoB;AAInD,OAAO,SAASC,YAAYC,KAAuB;IACjD,MAAMC,QAAQN;IACd,MAAMO,aAAaD,MAAME,OAAO,CAACC,IAAI,KAAK;IAE1C,MAAMC,iBAAiBX,QAAQ;QAC7B,OAAOI;IACT,GAAG,EAAE;IAEL,MAAMQ,kBAAkBZ,QAAQ;QAC9B,MAAMa,cAAcN,MAAME,OAAO,CAACC,IAAI,KAAK,UAAU,wBAAwB;QAE7E,OAAOP,WAAWI,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,KAACL;QACE,GAAGI,KAAK;QACTC,OAAOC,aAAa,SAAS;QAC7BY,YAAY;YACVC,aAAa;YACbC,qBAAqB;YACrBC,2BAA2B;YAC3BC,YAAY;YACZC,oBAAoB;QACtB;QACAC,YAAY;YAACvB,WAAWwB,YAAY;YAAEhB;YAAgBC;SAAgB;QACtEgB,aAAY;;AAGlB"}
1
+ {"version":3,"sources":["../../../src/components/logql-editor.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 { useTheme } from '@mui/material';\nimport CodeMirror, { EditorView, ReactCodeMirrorProps } from '@uiw/react-codemirror';\nimport { LogQLExtension, CompletionConfig } from './logql-extension';\n\nexport type LogQLEditorProps = Omit<ReactCodeMirrorProps, 'theme' | 'extensions'> & {\n completionConfig?: CompletionConfig;\n};\n\nexport function LogQLEditor(props: LogQLEditorProps): ReactElement {\n const { completionConfig, ...codemirrorProps } = props;\n const theme = useTheme();\n const isDarkMode = theme.palette.mode === 'dark';\n\n const logqlExtension = useMemo(() => {\n return LogQLExtension(completionConfig);\n }, [completionConfig]);\n\n const codemirrorTheme = useMemo(() => {\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',\n },\n '.cm-content': {\n padding: '8px',\n },\n });\n }, [theme]);\n\n return (\n <CodeMirror\n {...codemirrorProps}\n theme={isDarkMode ? 'dark' : 'light'}\n basicSetup={{\n lineNumbers: false,\n highlightActiveLine: false,\n highlightActiveLineGutter: false,\n foldGutter: false,\n syntaxHighlighting: true,\n }}\n extensions={[EditorView.lineWrapping, logqlExtension, codemirrorTheme]}\n placeholder='Example: {job=\"my-service\"} |= \"error\"'\n />\n );\n}\n"],"names":["useMemo","useTheme","CodeMirror","EditorView","LogQLExtension","LogQLEditor","props","completionConfig","codemirrorProps","theme","isDarkMode","palette","mode","logqlExtension","codemirrorTheme","borderColor","backgroundColor","border","borderRadius","shape","outline","padding","basicSetup","lineNumbers","highlightActiveLine","highlightActiveLineGutter","foldGutter","syntaxHighlighting","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,QAAQ,QAAQ,gBAAgB;AACzC,OAAOC,cAAcC,UAAU,QAA8B,wBAAwB;AACrF,SAASC,cAAc,QAA0B,oBAAoB;AAMrE,OAAO,SAASC,YAAYC,KAAuB;IACjD,MAAM,EAAEC,gBAAgB,EAAE,GAAGC,iBAAiB,GAAGF;IACjD,MAAMG,QAAQR;IACd,MAAMS,aAAaD,MAAME,OAAO,CAACC,IAAI,KAAK;IAE1C,MAAMC,iBAAiBb,QAAQ;QAC7B,OAAOI,eAAeG;IACxB,GAAG;QAACA;KAAiB;IAErB,MAAMO,kBAAkBd,QAAQ;QAC9B,MAAMe,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,KAACP;QACE,GAAGM,eAAe;QACnBC,OAAOC,aAAa,SAAS;QAC7BY,YAAY;YACVC,aAAa;YACbC,qBAAqB;YACrBC,2BAA2B;YAC3BC,YAAY;YACZC,oBAAoB;QACtB;QACAC,YAAY;YAACzB,WAAW0B,YAAY;YAAEhB;YAAgBC;SAAgB;QACtEgB,aAAY;;AAGlB"}
@@ -1,4 +1,12 @@
1
1
  import { LRLanguage } from '@codemirror/language';
2
2
  import { Extension } from '@uiw/react-codemirror';
3
- export declare function LogQLExtension(): Array<LRLanguage | Extension>;
3
+ import { AbsoluteTimeRange } from '@perses-dev/core';
4
+ import { LokiClient } from '../model';
5
+ export interface CompletionConfig {
6
+ /** a LokiClient instance, can be created with LokiDatasource.createClient() */
7
+ client?: LokiClient;
8
+ /** search for label values in a given time range */
9
+ timeRange?: AbsoluteTimeRange;
10
+ }
11
+ export declare function LogQLExtension(completionCfg?: CompletionConfig): Array<LRLanguage | Extension>;
4
12
  //# sourceMappingURL=logql-extension.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logql-extension.d.ts","sourceRoot":"","sources":["../../../src/components/logql-extension.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAelD,wBAAgB,cAAc,IAAI,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC,CAG9D"}
1
+ {"version":3,"file":"logql-extension.d.ts","sourceRoot":"","sources":["../../../src/components/logql-extension.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAgBtC,MAAM,WAAW,gBAAgB;IAC/B,+EAA+E;IAC/E,MAAM,CAAC,EAAE,UAAU,CAAC;IAEpB,oDAAoD;IACpD,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,wBAAgB,cAAc,CAAC,aAAa,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC,CAgB9F"}
@@ -13,6 +13,7 @@
13
13
  import { LRLanguage } from '@codemirror/language';
14
14
  import { parser } from '@grafana/lezer-logql';
15
15
  import { logqlHighlight } from './logql-highlight';
16
+ import { complete } from './complete';
16
17
  function logqlLanguage() {
17
18
  return LRLanguage.define({
18
19
  parser: parser.configure({
@@ -37,8 +38,21 @@ function logqlLanguage() {
37
38
  }
38
39
  });
39
40
  }
40
- export function LogQLExtension() {
41
+ export function LogQLExtension(completionCfg) {
41
42
  const language = logqlLanguage();
43
+ // Only add autocomplete if config is provided
44
+ if (completionCfg) {
45
+ const completion = language.data.of({
46
+ autocomplete: (ctx)=>complete(completionCfg, ctx).catch((e)=>{
47
+ console.error('error during LogQL auto-complete', e);
48
+ return null;
49
+ })
50
+ });
51
+ return [
52
+ language,
53
+ completion
54
+ ];
55
+ }
42
56
  return [
43
57
  language
44
58
  ];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/logql-extension.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\nimport { LRLanguage } from '@codemirror/language';\nimport { parser } from '@grafana/lezer-logql';\nimport { Extension } from '@uiw/react-codemirror';\nimport { logqlHighlight } from './logql-highlight';\n\nfunction logqlLanguage(): LRLanguage {\n return LRLanguage.define({\n parser: parser.configure({\n props: [logqlHighlight],\n }),\n languageData: {\n closeBrackets: { brackets: ['(', '[', '{', \"'\", '\"', '`'] },\n commentTokens: { line: '#' },\n },\n });\n}\n\nexport function LogQLExtension(): Array<LRLanguage | Extension> {\n const language = logqlLanguage();\n return [language];\n}\n"],"names":["LRLanguage","parser","logqlHighlight","logqlLanguage","define","configure","props","languageData","closeBrackets","brackets","commentTokens","line","LogQLExtension","language"],"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,SAASA,UAAU,QAAQ,uBAAuB;AAClD,SAASC,MAAM,QAAQ,uBAAuB;AAE9C,SAASC,cAAc,QAAQ,oBAAoB;AAEnD,SAASC;IACP,OAAOH,WAAWI,MAAM,CAAC;QACvBH,QAAQA,OAAOI,SAAS,CAAC;YACvBC,OAAO;gBAACJ;aAAe;QACzB;QACAK,cAAc;YACZC,eAAe;gBAAEC,UAAU;oBAAC;oBAAK;oBAAK;oBAAK;oBAAK;oBAAK;iBAAI;YAAC;YAC1DC,eAAe;gBAAEC,MAAM;YAAI;QAC7B;IACF;AACF;AAEA,OAAO,SAASC;IACd,MAAMC,WAAWV;IACjB,OAAO;QAACU;KAAS;AACnB"}
1
+ {"version":3,"sources":["../../../src/components/logql-extension.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\nimport { LRLanguage } from '@codemirror/language';\nimport { parser } from '@grafana/lezer-logql';\nimport { CompletionContext } from '@codemirror/autocomplete';\nimport { Extension } from '@uiw/react-codemirror';\nimport { AbsoluteTimeRange } from '@perses-dev/core';\nimport { LokiClient } from '../model';\nimport { logqlHighlight } from './logql-highlight';\nimport { complete } from './complete';\n\nfunction logqlLanguage(): LRLanguage {\n return LRLanguage.define({\n parser: parser.configure({\n props: [logqlHighlight],\n }),\n languageData: {\n closeBrackets: { brackets: ['(', '[', '{', \"'\", '\"', '`'] },\n commentTokens: { line: '#' },\n },\n });\n}\n\nexport interface CompletionConfig {\n /** a LokiClient instance, can be created with LokiDatasource.createClient() */\n client?: LokiClient;\n\n /** search for label values in a given time range */\n timeRange?: AbsoluteTimeRange;\n}\n\nexport function LogQLExtension(completionCfg?: CompletionConfig): Array<LRLanguage | Extension> {\n const language = logqlLanguage();\n\n // Only add autocomplete if config is provided\n if (completionCfg) {\n const completion = language.data.of({\n autocomplete: (ctx: CompletionContext) =>\n complete(completionCfg, ctx).catch((e) => {\n console.error('error during LogQL auto-complete', e);\n return null;\n }),\n });\n return [language, completion];\n }\n\n return [language];\n}\n"],"names":["LRLanguage","parser","logqlHighlight","complete","logqlLanguage","define","configure","props","languageData","closeBrackets","brackets","commentTokens","line","LogQLExtension","completionCfg","language","completion","data","of","autocomplete","ctx","catch","e","console","error"],"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,SAASA,UAAU,QAAQ,uBAAuB;AAClD,SAASC,MAAM,QAAQ,uBAAuB;AAK9C,SAASC,cAAc,QAAQ,oBAAoB;AACnD,SAASC,QAAQ,QAAQ,aAAa;AAEtC,SAASC;IACP,OAAOJ,WAAWK,MAAM,CAAC;QACvBJ,QAAQA,OAAOK,SAAS,CAAC;YACvBC,OAAO;gBAACL;aAAe;QACzB;QACAM,cAAc;YACZC,eAAe;gBAAEC,UAAU;oBAAC;oBAAK;oBAAK;oBAAK;oBAAK;oBAAK;iBAAI;YAAC;YAC1DC,eAAe;gBAAEC,MAAM;YAAI;QAC7B;IACF;AACF;AAUA,OAAO,SAASC,eAAeC,aAAgC;IAC7D,MAAMC,WAAWX;IAEjB,8CAA8C;IAC9C,IAAIU,eAAe;QACjB,MAAME,aAAaD,SAASE,IAAI,CAACC,EAAE,CAAC;YAClCC,cAAc,CAACC,MACbjB,SAASW,eAAeM,KAAKC,KAAK,CAAC,CAACC;oBAClCC,QAAQC,KAAK,CAAC,oCAAoCF;oBAClD,OAAO;gBACT;QACJ;QACA,OAAO;YAACP;YAAUC;SAAW;IAC/B;IAEA,OAAO;QAACD;KAAS;AACnB"}
@@ -1,3 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  // Copyright 2025 The Perses Authors
2
3
  // Licensed under the Apache License, Version 2.0 (the "License");
3
4
  // you may not use this file except in compliance with the License.
@@ -10,7 +11,6 @@
10
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
12
  // See the License for the specific language governing permissions and
12
13
  // limitations under the License.
13
- import { jsx as _jsx } from "react/jsx-runtime";
14
14
  import { HTTPSettingsEditor } from '@perses-dev/plugin-system';
15
15
  export function LokiDatasourceEditor(props) {
16
16
  const { value, onChange, isReadonly } = props;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/datasources/loki-datasource/LokiDatasourceEditor.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 { HTTPSettingsEditor } from '@perses-dev/plugin-system';\nimport { ReactElement } from 'react';\nimport { LokiDatasourceSpec } from './loki-datasource-types';\n\nexport interface LokiDatasourceEditorProps {\n value: LokiDatasourceSpec;\n onChange: (next: LokiDatasourceSpec) => void;\n isReadonly?: boolean;\n}\n\nexport function LokiDatasourceEditor(props: LokiDatasourceEditorProps): ReactElement {\n const { value, onChange, isReadonly } = props;\n\n const initialSpecDirect: LokiDatasourceSpec = {\n directUrl: '',\n };\n\n const initialSpecProxy: LokiDatasourceSpec = {\n proxy: {\n kind: 'HTTPProxy',\n spec: {\n allowedEndpoints: [\n {\n endpointPattern: '/loki/api/v1/query',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/query_range',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/labels',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/label/([a-zA-Z0-9_-]+)/values',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/series',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/index/volume',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/index/volume_range',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/index/stats',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/tail',\n method: 'GET',\n },\n ],\n url: '',\n },\n },\n };\n\n return (\n <HTTPSettingsEditor\n value={value}\n onChange={onChange}\n isReadonly={isReadonly}\n initialSpecDirect={initialSpecDirect}\n initialSpecProxy={initialSpecProxy}\n />\n );\n}\n"],"names":["HTTPSettingsEditor","LokiDatasourceEditor","props","value","onChange","isReadonly","initialSpecDirect","directUrl","initialSpecProxy","proxy","kind","spec","allowedEndpoints","endpointPattern","method","url"],"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,SAASA,kBAAkB,QAAQ,4BAA4B;AAU/D,OAAO,SAASC,qBAAqBC,KAAgC;IACnE,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,UAAU,EAAE,GAAGH;IAExC,MAAMI,oBAAwC;QAC5CC,WAAW;IACb;IAEA,MAAMC,mBAAuC;QAC3CC,OAAO;YACLC,MAAM;YACNC,MAAM;gBACJC,kBAAkB;oBAChB;wBACEC,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;iBACD;gBACDC,KAAK;YACP;QACF;IACF;IAEA,qBACE,KAACf;QACCG,OAAOA;QACPC,UAAUA;QACVC,YAAYA;QACZC,mBAAmBA;QACnBE,kBAAkBA;;AAGxB"}
1
+ {"version":3,"sources":["../../../../src/datasources/loki-datasource/LokiDatasourceEditor.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 { HTTPSettingsEditor } from '@perses-dev/plugin-system';\nimport { ReactElement } from 'react';\nimport { LokiDatasourceSpec } from './loki-datasource-types';\n\nexport interface LokiDatasourceEditorProps {\n value: LokiDatasourceSpec;\n onChange: (next: LokiDatasourceSpec) => void;\n isReadonly?: boolean;\n}\n\nexport function LokiDatasourceEditor(props: LokiDatasourceEditorProps): ReactElement {\n const { value, onChange, isReadonly } = props;\n\n const initialSpecDirect: LokiDatasourceSpec = {\n directUrl: '',\n };\n\n const initialSpecProxy: LokiDatasourceSpec = {\n proxy: {\n kind: 'HTTPProxy',\n spec: {\n allowedEndpoints: [\n {\n endpointPattern: '/loki/api/v1/query',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/query_range',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/labels',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/label/([a-zA-Z0-9_-]+)/values',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/series',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/index/volume',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/index/volume_range',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/index/stats',\n method: 'GET',\n },\n {\n endpointPattern: '/loki/api/v1/tail',\n method: 'GET',\n },\n ],\n url: '',\n },\n },\n };\n\n return (\n <HTTPSettingsEditor\n value={value}\n onChange={onChange}\n isReadonly={isReadonly}\n initialSpecDirect={initialSpecDirect}\n initialSpecProxy={initialSpecProxy}\n />\n );\n}\n"],"names":["HTTPSettingsEditor","LokiDatasourceEditor","props","value","onChange","isReadonly","initialSpecDirect","directUrl","initialSpecProxy","proxy","kind","spec","allowedEndpoints","endpointPattern","method","url"],"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,SAASA,kBAAkB,QAAQ,4BAA4B;AAU/D,OAAO,SAASC,qBAAqBC,KAAgC;IACnE,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,UAAU,EAAE,GAAGH;IAExC,MAAMI,oBAAwC;QAC5CC,WAAW;IACb;IAEA,MAAMC,mBAAuC;QAC3CC,OAAO;YACLC,MAAM;YACNC,MAAM;gBACJC,kBAAkB;oBAChB;wBACEC,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;oBACA;wBACED,iBAAiB;wBACjBC,QAAQ;oBACV;iBACD;gBACDC,KAAK;YACP;QACF;IACF;IAEA,qBACE,KAACf;QACCG,OAAOA;QACPC,UAAUA;QACVC,YAAYA;QACZC,mBAAmBA;QACnBE,kBAAkBA;;AAGxB"}