@grafana/plugin-ui 0.14.1-dsconfig.4bdebf6 → 0.14.1-dsconfig.9143abf

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 (108) hide show
  1. package/dist/cjs/index.cjs +971 -277
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.d.cts +91 -6
  4. package/dist/cjs/schemas.cjs +2340 -883
  5. package/dist/cjs/schemas.cjs.map +1 -1
  6. package/dist/cjs/schemas.d.cts +36 -1
  7. package/dist/cjs/test-utils.d.cts +23 -23
  8. package/dist/esm/components/ConfigEditor/ConfigSection/ConfigDescriptionLink.js +2 -2
  9. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/DatasourceConfigWizard.js +14 -9
  10. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/DatasourceConfigWizard.js.map +1 -1
  11. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/SchemaField.js +16 -5
  12. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/SchemaField.js.map +1 -1
  13. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/SidebarNav.js.map +1 -1
  14. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/TabLayout.js +9 -4
  15. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/TabLayout.js.map +1 -1
  16. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/fieldUtils.js +61 -11
  17. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/fieldUtils.js.map +1 -1
  18. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/{useDatasourceConfigForm.js → hooks/useDatasourceConfigForm.js} +71 -29
  19. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/hooks/useDatasourceConfigForm.js.map +1 -0
  20. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/hooks/useHealthChecks.js.map +1 -0
  21. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/{AuthorizationHeaderField.js → inputs/AuthorizationHeaderField.js} +6 -8
  22. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/AuthorizationHeaderField.js.map +1 -0
  23. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/{ComplexFieldNote.js → inputs/ComplexFieldNote.js} +6 -4
  24. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/ComplexFieldNote.js.map +1 -0
  25. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/FileUploadField.js +106 -0
  26. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/FileUploadField.js.map +1 -0
  27. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/{IndexedPairEditor.js → inputs/IndexedPairEditor.js} +52 -52
  28. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/IndexedPairEditor.js.map +1 -0
  29. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/ObjectArrayEditor.js +190 -0
  30. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/ObjectArrayEditor.js.map +1 -0
  31. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/{PdcFieldNote.js → inputs/PdcFieldNote.js} +5 -8
  32. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/PdcFieldNote.js.map +1 -0
  33. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/{SecureFieldInput.js → inputs/SecureFieldInput.js} +8 -10
  34. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/SecureFieldInput.js.map +1 -0
  35. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/StringArrayInput.js +80 -0
  36. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/inputs/StringArrayInput.js.map +1 -0
  37. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/renderFieldInput.js +76 -41
  38. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/renderFieldInput.js.map +1 -1
  39. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/tabStyles.js +4 -4
  40. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/tabStyles.js.map +1 -1
  41. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/wizardStyles.js +3 -3
  42. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/wizardStyles.js.map +1 -1
  43. package/dist/esm/components/DataLinks/DataLink.js +2 -2
  44. package/dist/esm/components/DataLinks/DataLinks.js +2 -2
  45. package/dist/esm/components/DatePicker/styles.js +2 -2
  46. package/dist/esm/components/DatePickerWithInput/styles.js +2 -2
  47. package/dist/esm/components/QueryEditor/EditorField.js +2 -2
  48. package/dist/esm/components/QueryEditor/EditorHeader.js +2 -2
  49. package/dist/esm/components/QueryEditor/EditorRow.js +2 -2
  50. package/dist/esm/components/QueryEditor/EditorSwitch.js +2 -2
  51. package/dist/esm/components/QueryEditor/InputGroup.js +2 -2
  52. package/dist/esm/components/QueryEditor/Space.js +2 -2
  53. package/dist/esm/components/QueryEditor/query-editor-raw/RawEditor.js +2 -2
  54. package/dist/esm/components/QueryEditor/visual-query-builder/EditorField.js +2 -2
  55. package/dist/esm/components/QueryEditor/visual-query-builder/EditorRow.js +2 -2
  56. package/dist/esm/components/QueryEditor/visual-query-builder/Preview.js +2 -2
  57. package/dist/esm/components/QueryEditor/visual-query-builder/SelectRow.js +2 -2
  58. package/dist/esm/datasource/schema/cel.js +42 -0
  59. package/dist/esm/datasource/schema/cel.js.map +1 -0
  60. package/dist/esm/datasource/schema/config.js +37 -14
  61. package/dist/esm/datasource/schema/config.js.map +1 -1
  62. package/dist/esm/datasource/schema/datasource.js +129 -3
  63. package/dist/esm/datasource/schema/datasource.js.map +1 -1
  64. package/dist/esm/datasource/schema/datasources/cloudwatch.schema.json.js +3 -3
  65. package/dist/esm/datasource/schema/datasources/grafana-bigquery-datasource.schema.json.js +407 -58
  66. package/dist/esm/datasource/schema/datasources/grafana-bigquery-datasource.schema.json.js.map +1 -1
  67. package/dist/esm/datasource/schema/datasources/grafana-clickhouse-datasource.schema.json.js +206 -44
  68. package/dist/esm/datasource/schema/datasources/grafana-clickhouse-datasource.schema.json.js.map +1 -1
  69. package/dist/esm/datasource/schema/datasources/grafana-googlesheets-datasource.schema.json.js +103 -107
  70. package/dist/esm/datasource/schema/datasources/grafana-googlesheets-datasource.schema.json.js.map +1 -1
  71. package/dist/esm/datasource/schema/datasources/grafana-mqtt-datasource.schema.json.js +3 -3
  72. package/dist/esm/datasource/schema/datasources/grafana-postgresql-datasource.schema.json.js +170 -223
  73. package/dist/esm/datasource/schema/datasources/grafana-postgresql-datasource.schema.json.js.map +1 -1
  74. package/dist/esm/datasource/schema/datasources/influxdb.schema.json.js +22 -8
  75. package/dist/esm/datasource/schema/datasources/influxdb.schema.json.js.map +1 -1
  76. package/dist/esm/datasource/schema/datasources/loki.schema.json.js +3 -3
  77. package/dist/esm/datasource/schema/datasources/mysql.schema.json.js +221 -54
  78. package/dist/esm/datasource/schema/datasources/mysql.schema.json.js.map +1 -1
  79. package/dist/esm/datasource/schema/datasources/prometheus.schema.json.js +1 -1
  80. package/dist/esm/datasource/schema/datasources/stackdriver.schema.json.js +132 -43
  81. package/dist/esm/datasource/schema/datasources/stackdriver.schema.json.js.map +1 -1
  82. package/dist/esm/datasource/schema/datasources/tempo.schema.json.js +52 -6
  83. package/dist/esm/datasource/schema/datasources/tempo.schema.json.js.map +1 -1
  84. package/dist/esm/datasource/schema/datasources/yesoreyeram-infinity-datasource.schema.json.js +783 -96
  85. package/dist/esm/datasource/schema/datasources/yesoreyeram-infinity-datasource.schema.json.js.map +1 -1
  86. package/dist/esm/datasource/schema/dsconfig.js +17 -0
  87. package/dist/esm/datasource/schema/dsconfig.js.map +1 -0
  88. package/dist/esm/datasource/schema/{llmHints.js → llm.js} +1 -1
  89. package/dist/esm/datasource/schema/llm.js.map +1 -0
  90. package/dist/esm/datasource/schema/{provisioningYaml.js → provisioning.js} +78 -2
  91. package/dist/esm/datasource/schema/provisioning.js.map +1 -0
  92. package/dist/esm/index.d.ts +91 -6
  93. package/dist/esm/index.js +5 -5
  94. package/dist/esm/schemas.d.ts +36 -1
  95. package/dist/esm/test-utils.d.ts +23 -23
  96. package/package.json +9 -6
  97. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/AuthorizationHeaderField.js.map +0 -1
  98. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/ComplexFieldNote.js.map +0 -1
  99. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/IndexedPairEditor.js.map +0 -1
  100. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/PdcFieldNote.js.map +0 -1
  101. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/SecureFieldInput.js.map +0 -1
  102. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/StringArrayInput.js +0 -84
  103. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/StringArrayInput.js.map +0 -1
  104. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/useDatasourceConfigForm.js.map +0 -1
  105. package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/useHealthChecks.js.map +0 -1
  106. package/dist/esm/datasource/schema/llmHints.js.map +0 -1
  107. package/dist/esm/datasource/schema/provisioningYaml.js.map +0 -1
  108. /package/dist/esm/components/ConfigEditor/DatasourceConfigWizard/{useHealthChecks.js → hooks/useHealthChecks.js} +0 -0
@@ -17,12 +17,13 @@ var AutoSizer = require('react-virtualized-auto-sizer');
17
17
  var sqlFormatter = require('sql-formatter');
18
18
  var rxjs = require('rxjs');
19
19
  var operators$1 = require('rxjs/operators');
20
+ var celJs = require('@marcbachmann/cel-js');
20
21
  var reactHookForm = require('react-hook-form');
21
22
  var dnd = require('@hello-pangea/dnd');
22
23
  var reactPopperTooltip = require('react-popper-tooltip');
23
24
  var Prism = require('prismjs');
24
- var yaml = require('js-yaml');
25
25
  var schemas = require('./schemas.cjs');
26
+ var yaml = require('js-yaml');
26
27
 
27
28
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
28
29
 
@@ -79,7 +80,7 @@ function Segment(props) {
79
80
  );
80
81
  }
81
82
 
82
- const getStyles$v = (theme) => {
83
+ const getStyles$x = (theme) => {
83
84
  const containerBorder = theme.isDark ? theme.v1.palette.dark9 : theme.v1.palette.gray5;
84
85
  return {
85
86
  container: css.css`
@@ -229,7 +230,7 @@ const getBodyStyles = (theme) => {
229
230
 
230
231
  const DatePicker = React.memo((props) => {
231
232
  const theme = ui.useTheme2();
232
- const styles = getStyles$v(theme);
233
+ const styles = getStyles$x(theme);
233
234
  const { isOpen, onClose } = props;
234
235
  if (!isOpen) {
235
236
  return null;
@@ -259,7 +260,7 @@ const Body = React.memo(({ value, onChange }) => {
259
260
  });
260
261
  Body.displayName = "Body";
261
262
 
262
- function getStyles$u() {
263
+ function getStyles$w() {
263
264
  return {
264
265
  input: css.css({
265
266
  /* hides the native Calendar picker icon given when using type=date */
@@ -275,7 +276,7 @@ const formatDate = (date) => date.toISOString().split("T")[0];
275
276
  const DatePickerWithInput = (props) => {
276
277
  const { value, onChange, className, ...rest } = props;
277
278
  const [open, setOpen] = React.useState(false);
278
- const styles = getStyles$u();
279
+ const styles = getStyles$w();
279
280
  return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement(
280
281
  ui.Input,
281
282
  {
@@ -437,7 +438,7 @@ __publicField$4(DataSourcePicker, "defaultProps", {
437
438
  });
438
439
 
439
440
  const { FormField, Switch } = ui.LegacyForms;
440
- const getStyles$t = ui.stylesFactory(() => ({
441
+ const getStyles$v = ui.stylesFactory(() => ({
441
442
  firstRow: css.css`
442
443
  display: flex;
443
444
  `,
@@ -454,7 +455,7 @@ const getStyles$t = ui.stylesFactory(() => ({
454
455
  }));
455
456
  const DataLink = (props) => {
456
457
  const { value, onChange, onDelete, suggestions, className } = props;
457
- const styles = getStyles$t();
458
+ const styles = getStyles$v();
458
459
  const [showInternalLink, setShowInternalLink] = useInternalLink(value.datasourceUid);
459
460
  const handleChange = (field) => (event) => {
460
461
  onChange({
@@ -571,7 +572,7 @@ function useInternalLink(datasourceUid) {
571
572
  return [showInternalLink, setShowInternalLink];
572
573
  }
573
574
 
574
- const getStyles$s = (theme) => ({
575
+ const getStyles$u = (theme) => ({
575
576
  infoText: css.css`
576
577
  padding-bottom: ${theme.v1.spacing.md};
577
578
  color: ${theme.v1.colors.textWeak};
@@ -583,7 +584,7 @@ const getStyles$s = (theme) => ({
583
584
  const DataLinks = (props) => {
584
585
  const { value, onChange } = props;
585
586
  const theme = ui.useTheme2();
586
- const styles = getStyles$s(theme);
587
+ const styles = getStyles$u(theme);
587
588
  return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement("h3", { className: "page-heading" }, "Data links"), /* @__PURE__ */ React__default.default.createElement("div", { className: styles.infoText }, "Add links to existing fields. Links will be shown in log row details next to the field value."), /* @__PURE__ */ React__default.default.createElement("div", { className: "gf-form-group" }, value && value.map((field, index) => {
588
589
  return /* @__PURE__ */ React__default.default.createElement(
589
590
  DataLink,
@@ -5479,7 +5480,7 @@ const getButtonStyles = (theme) => ({
5479
5480
  });
5480
5481
 
5481
5482
  const InputGroup = ({ children }) => {
5482
- const styles = ui.useStyles2(getStyles$r);
5483
+ const styles = ui.useStyles2(getStyles$t);
5483
5484
  const modifiedChildren = React.Children.map(children, (child) => {
5484
5485
  if (React.isValidElement(child) && child.props.invalid) {
5485
5486
  return React.cloneElement(child, { className: css.cx(child.props.className, styles.invalidChild) });
@@ -5497,7 +5498,7 @@ const borderPriority = [
5497
5498
  "focused"
5498
5499
  // highest priority
5499
5500
  ];
5500
- const getStyles$r = () => ({
5501
+ const getStyles$t = () => ({
5501
5502
  root: css.css({
5502
5503
  display: "flex",
5503
5504
  // Style the direct children of the component
@@ -5580,10 +5581,10 @@ const EditorFieldGroup = ({ children }) => {
5580
5581
  };
5581
5582
 
5582
5583
  const EditorHeader = ({ children }) => {
5583
- const styles = ui.useStyles2(getStyles$q);
5584
+ const styles = ui.useStyles2(getStyles$s);
5584
5585
  return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.root }, children);
5585
5586
  };
5586
- const getStyles$q = (theme) => ({
5587
+ const getStyles$s = (theme) => ({
5587
5588
  root: css.css({
5588
5589
  display: "flex",
5589
5590
  flexWrap: "wrap",
@@ -5596,12 +5597,12 @@ const getStyles$q = (theme) => ({
5596
5597
  const EditorField$1 = (props) => {
5597
5598
  var _a;
5598
5599
  const { label, optional, tooltip, tooltipInteractive, children, width, ...fieldProps } = props;
5599
- const styles = ui.useStyles2(getStyles$p, width);
5600
+ const styles = ui.useStyles2(getStyles$r, width);
5600
5601
  const childInputId = (fieldProps == null ? void 0 : fieldProps.htmlFor) || ((_a = ui.ReactUtils) == null ? void 0 : _a.getChildId(children));
5601
5602
  const labelEl = /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement("label", { className: styles.label, htmlFor: childInputId }, label, optional && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.optional }, " - optional"), tooltip && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { placement: "top", content: tooltip, theme: "info", interactive: tooltipInteractive }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { tabIndex: 0, name: "info-circle", size: "sm", className: styles.icon }))), /* @__PURE__ */ React__default.default.createElement(ui.Space, { v: 0.5 }));
5602
5603
  return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.root }, /* @__PURE__ */ React__default.default.createElement(ui.Field, { className: styles.field, label: labelEl, ...fieldProps }, children));
5603
5604
  };
5604
- const getStyles$p = (theme, width) => {
5605
+ const getStyles$r = (theme, width) => {
5605
5606
  return {
5606
5607
  root: css.css({
5607
5608
  minWidth: theme.spacing(width != null ? width : 0)
@@ -5629,10 +5630,10 @@ const getStyles$p = (theme, width) => {
5629
5630
  };
5630
5631
 
5631
5632
  const EditorRow$1 = ({ children }) => {
5632
- const styles = ui.useStyles2(getStyles$o);
5633
+ const styles = ui.useStyles2(getStyles$q);
5633
5634
  return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.root }, /* @__PURE__ */ React__default.default.createElement(EditorStack, { gap: 2 }, children));
5634
5635
  };
5635
- const getStyles$o = (theme) => {
5636
+ const getStyles$q = (theme) => {
5636
5637
  return {
5637
5638
  root: css.css({
5638
5639
  padding: theme.spacing(1),
@@ -5647,10 +5648,10 @@ const EditorRows$1 = ({ children }) => {
5647
5648
  };
5648
5649
 
5649
5650
  const EditorSwitch = (props) => {
5650
- const styles = getStyles$n();
5651
+ const styles = getStyles$p();
5651
5652
  return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.switch }, /* @__PURE__ */ React__default.default.createElement(ui.Switch, { ...props }));
5652
5653
  };
5653
- const getStyles$n = () => {
5654
+ const getStyles$p = () => {
5654
5655
  return {
5655
5656
  switch: css.css({
5656
5657
  display: "flex",
@@ -5708,10 +5709,10 @@ const getSelectStyles = (theme) => ({
5708
5709
  });
5709
5710
 
5710
5711
  const Space = ({ v = 0, h = 0, layout = "block" }) => {
5711
- const styles = ui.useStyles2(getStyles$m, v, h, layout);
5712
+ const styles = ui.useStyles2(getStyles$o, v, h, layout);
5712
5713
  return /* @__PURE__ */ React__default.default.createElement("span", { className: css.cx(styles.wrapper) });
5713
5714
  };
5714
- const getStyles$m = (theme, v, h, layout) => ({
5715
+ const getStyles$o = (theme, v, h, layout) => ({
5715
5716
  wrapper: css.css([
5716
5717
  {
5717
5718
  paddingRight: theme.spacing(h != null ? h : 0),
@@ -6323,7 +6324,7 @@ function RawEditor({
6323
6324
  language = "sql"
6324
6325
  }) {
6325
6326
  const theme = ui.useTheme2();
6326
- const styles = ui.useStyles2(getStyles$l);
6327
+ const styles = ui.useStyles2(getStyles$n);
6327
6328
  const [isExpanded, setIsExpanded] = React.useState(false);
6328
6329
  const [toolboxRef, toolboxMeasure] = reactUse.useMeasure();
6329
6330
  const [editorRef, editorMeasure] = reactUse.useMeasure();
@@ -6393,7 +6394,7 @@ function RawEditor({
6393
6394
  renderEditor(true)
6394
6395
  ));
6395
6396
  }
6396
- function getStyles$l(theme) {
6397
+ function getStyles$n(theme) {
6397
6398
  return {
6398
6399
  modal: css.css`
6399
6400
  width: 95vw;
@@ -6408,7 +6409,7 @@ function getStyles$l(theme) {
6408
6409
 
6409
6410
  function Preview({ rawSql, language = "sql" }) {
6410
6411
  const [_, copyToClipboard] = reactUse.useCopyToClipboard();
6411
- const styles = ui.useStyles2(getStyles$k);
6412
+ const styles = ui.useStyles2(getStyles$m);
6412
6413
  const labelElement = /* @__PURE__ */ React__default.default.createElement("div", { className: styles.labelWrapper }, /* @__PURE__ */ React__default.default.createElement("label", { className: styles.label }, "Preview"), /* @__PURE__ */ React__default.default.createElement(ui.IconButton, { tooltip: "Copy to clipboard", onClick: () => copyToClipboard(rawSql), name: "copy" }));
6413
6414
  return /* @__PURE__ */ React__default.default.createElement(ui.Field, { label: labelElement, className: styles.grow }, /* @__PURE__ */ React__default.default.createElement(
6414
6415
  ui.CodeEditor,
@@ -6422,7 +6423,7 @@ function Preview({ rawSql, language = "sql" }) {
6422
6423
  }
6423
6424
  ));
6424
6425
  }
6425
- function getStyles$k(theme) {
6426
+ function getStyles$m(theme) {
6426
6427
  return {
6427
6428
  grow: css.css({ flexGrow: 1 }),
6428
6429
  label: css.css({ fontSize: 12, fontWeight: theme.typography.fontWeightMedium }),
@@ -6541,7 +6542,7 @@ function SQLOrderByRow({ fields, query, onQueryChange, db }) {
6541
6542
  const asteriskValue = { label: "*", value: "*" };
6542
6543
  function SelectRow({ sql, columns, onSqlChange, functions }) {
6543
6544
  var _a;
6544
- const styles = ui.useStyles2(getStyles$j);
6545
+ const styles = ui.useStyles2(getStyles$l);
6545
6546
  const columnsWithAsterisk = [asteriskValue, ...columns || []];
6546
6547
  const onColumnChange = React.useCallback(
6547
6548
  (item, index) => (column) => {
@@ -6637,7 +6638,7 @@ function SelectRow({ sql, columns, onSqlChange, functions }) {
6637
6638
  }
6638
6639
  ));
6639
6640
  }
6640
- const getStyles$j = () => {
6641
+ const getStyles$l = () => {
6641
6642
  return { addButton: css.css({ alignSelf: "flex-start" }) };
6642
6643
  };
6643
6644
  function getColumnValue({ parameters }) {
@@ -6983,10 +6984,10 @@ function mapFieldsToTypes(columns) {
6983
6984
  }
6984
6985
 
6985
6986
  const EditorRow = ({ children }) => {
6986
- const styles = ui.useStyles2(getStyles$i);
6987
+ const styles = ui.useStyles2(getStyles$k);
6987
6988
  return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.root }, /* @__PURE__ */ React__default.default.createElement(EditorStack, { gap: 2 }, children));
6988
6989
  };
6989
- const getStyles$i = (theme) => {
6990
+ const getStyles$k = (theme) => {
6990
6991
  return {
6991
6992
  root: css.css({
6992
6993
  padding: theme.spacing(1),
@@ -7000,12 +7001,12 @@ const EditorField = (props) => {
7000
7001
  var _a;
7001
7002
  const { label, optional, tooltip, children, width, ...fieldProps } = props;
7002
7003
  const theme = ui.useTheme2();
7003
- const styles = getStyles$h(theme, width);
7004
+ const styles = getStyles$j(theme, width);
7004
7005
  const childInputId = (fieldProps == null ? void 0 : fieldProps.htmlFor) || ((_a = ui.ReactUtils) == null ? void 0 : _a.getChildId(children));
7005
7006
  const labelEl = /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement("label", { className: styles.label, htmlFor: childInputId }, label, optional && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.optional }, " - optional"), tooltip && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { placement: "top", content: tooltip, theme: "info" }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "info-circle", size: "sm", className: styles.icon }))), /* @__PURE__ */ React__default.default.createElement(Space, { v: 0.5 }));
7006
7007
  return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.root }, /* @__PURE__ */ React__default.default.createElement(ui.Field, { className: styles.field, label: labelEl, ...fieldProps }, children));
7007
7008
  };
7008
- const getStyles$h = ui.stylesFactory((theme, width) => {
7009
+ const getStyles$j = ui.stylesFactory((theme, width) => {
7009
7010
  return {
7010
7011
  root: css.css({
7011
7012
  minWidth: theme.spacing(width != null ? width : 0)
@@ -7455,7 +7456,7 @@ const ConfigSubSection = ({ children, ...props }) => {
7455
7456
  function ConfigDescriptionLink(props) {
7456
7457
  const { description, suffix, feature } = props;
7457
7458
  const text = `Learn more about ${feature}`;
7458
- const styles = ui.useStyles2(getStyles$g);
7459
+ const styles = ui.useStyles2(getStyles$i);
7459
7460
  return /* @__PURE__ */ React__default.default.createElement("span", { className: styles.container }, description, /* @__PURE__ */ React__default.default.createElement(
7460
7461
  "a",
7461
7462
  {
@@ -7467,7 +7468,7 @@ function ConfigDescriptionLink(props) {
7467
7468
  text
7468
7469
  ));
7469
7470
  }
7470
- const getStyles$g = (theme) => {
7471
+ const getStyles$i = (theme) => {
7471
7472
  return {
7472
7473
  container: css.css({
7473
7474
  color: theme.colors.text.secondary,
@@ -8436,6 +8437,44 @@ const AdvancedHttpSettings = ({
8436
8437
  ));
8437
8438
  };
8438
8439
 
8440
+ const exprCache = /* @__PURE__ */ new Map();
8441
+ function getCompiled(expr) {
8442
+ if (exprCache.has(expr)) {
8443
+ return exprCache.get(expr);
8444
+ }
8445
+ try {
8446
+ const compiled = celJs.parse(expr);
8447
+ exprCache.set(expr, compiled);
8448
+ return compiled;
8449
+ } catch (e) {
8450
+ exprCache.set(expr, null);
8451
+ return null;
8452
+ }
8453
+ }
8454
+ function evaluateCelExpression(expr, context, defaultValue = false) {
8455
+ if (!expr || !expr.trim()) {
8456
+ return defaultValue;
8457
+ }
8458
+ const compiled = getCompiled(expr);
8459
+ if (!compiled) {
8460
+ return defaultValue;
8461
+ }
8462
+ try {
8463
+ const result = compiled(context);
8464
+ return !!result;
8465
+ } catch (e) {
8466
+ return defaultValue;
8467
+ }
8468
+ }
8469
+ function extractFieldRefs(expr) {
8470
+ const matches = expr.matchAll(/([\w.]+)\s*(?:==|!=)/g);
8471
+ const refs = /* @__PURE__ */ new Set();
8472
+ for (const m of matches) {
8473
+ refs.add(m[1]);
8474
+ }
8475
+ return [...refs];
8476
+ }
8477
+
8439
8478
  function resolveGroups(schema) {
8440
8479
  const fieldMap = /* @__PURE__ */ new Map();
8441
8480
  for (const f of schema.fields) {
@@ -8459,18 +8498,18 @@ function resolveGroups(schema) {
8459
8498
  ];
8460
8499
  }
8461
8500
  function resolveRequiredFieldsGroup(schema) {
8462
- var _a;
8501
+ var _a, _b;
8463
8502
  const fieldMap = /* @__PURE__ */ new Map();
8464
8503
  for (const f of schema.fields) {
8465
8504
  fieldMap.set(f.id, f);
8466
8505
  }
8467
8506
  const requiredIds = /* @__PURE__ */ new Set();
8468
8507
  for (const f of schema.fields) {
8469
- if (f.required) {
8508
+ if (f.required || ((_a = f.tags) == null ? void 0 : _a.includes("pinned"))) {
8470
8509
  requiredIds.add(f.id);
8471
8510
  }
8472
8511
  }
8473
- const authGroup = (_a = schema.groups) == null ? void 0 : _a.find((g) => g.id === "auth");
8512
+ const authGroup = (_b = schema.groups) == null ? void 0 : _b.find((g) => g.id === "auth");
8474
8513
  if (authGroup) {
8475
8514
  for (const ref of authGroup.fieldRefs) {
8476
8515
  if (fieldMap.has(ref)) {
@@ -8486,16 +8525,17 @@ function resolveRequiredFieldsGroup(schema) {
8486
8525
  includeIds.add(id);
8487
8526
  const f = fieldMap.get(id);
8488
8527
  if (f == null ? void 0 : f.dependsOn) {
8489
- const parsed = parseDependsOn(f.dependsOn);
8490
- if (parsed && fieldMap.has(parsed.field)) {
8491
- includeIds.add(parsed.field);
8528
+ for (const ref of extractFieldRefs(f.dependsOn)) {
8529
+ if (fieldMap.has(ref)) {
8530
+ includeIds.add(ref);
8531
+ }
8492
8532
  }
8493
8533
  }
8494
8534
  }
8495
8535
  for (const f of schema.fields) {
8496
8536
  if (f.dependsOn && !includeIds.has(f.id)) {
8497
- const parsed = parseDependsOn(f.dependsOn);
8498
- if (parsed && requiredIds.has(parsed.field)) {
8537
+ const refs = extractFieldRefs(f.dependsOn);
8538
+ if (refs.some((ref) => requiredIds.has(ref))) {
8499
8539
  includeIds.add(f.id);
8500
8540
  }
8501
8541
  }
@@ -8507,7 +8547,7 @@ function resolveRequiredFieldsGroup(schema) {
8507
8547
  return {
8508
8548
  group: {
8509
8549
  id: "_required",
8510
- title: "Required",
8550
+ title: "General",
8511
8551
  fieldRefs: virtualFields.map((f) => f.id)
8512
8552
  },
8513
8553
  fields: virtualFields
@@ -8516,16 +8556,36 @@ function resolveRequiredFieldsGroup(schema) {
8516
8556
  function formKey(field) {
8517
8557
  return field.section ? `${field.section}.${field.key}` : field.key;
8518
8558
  }
8559
+ function getWatchedValue(watchedValues, key) {
8560
+ if (key.includes(".")) {
8561
+ const parts = key.split(".");
8562
+ let current = watchedValues;
8563
+ for (const part of parts) {
8564
+ if (current == null || typeof current !== "object") {
8565
+ return watchedValues[key];
8566
+ }
8567
+ current = current[part];
8568
+ }
8569
+ if (current !== void 0) {
8570
+ return current;
8571
+ }
8572
+ return watchedValues[key];
8573
+ }
8574
+ return watchedValues[key];
8575
+ }
8519
8576
  function parseDependsOn(expr) {
8520
8577
  var _a, _b;
8521
- const m = expr.match(/^(.+?)\s*==\s*(?:'([^']*)'|"([^"]*)"|(.+))$/);
8578
+ const m = expr.match(/^([\w.]+)\s*(==|!=)\s*(?:'([^']*)'|"([^"]*)"|(\S+))$/);
8522
8579
  if (!m) {
8523
8580
  return null;
8524
8581
  }
8525
- return { field: m[1].trim(), value: ((_b = (_a = m[2]) != null ? _a : m[3]) != null ? _b : m[4]).trim() };
8582
+ return { field: m[1], value: ((_b = (_a = m[3]) != null ? _a : m[4]) != null ? _b : m[5]).trim(), negate: m[2] === "!=" };
8583
+ }
8584
+ function evaluateDependsOn(parsed, currentValue) {
8585
+ const matches = String(currentValue != null ? currentValue : "") === parsed.value;
8586
+ return parsed.negate ? !matches : matches;
8526
8587
  }
8527
8588
  function resolveActiveOverride(field, watchedValues, fieldById) {
8528
- var _a;
8529
8589
  if (!field.overrides || field.overrides.length === 0) {
8530
8590
  return void 0;
8531
8591
  }
@@ -8536,7 +8596,7 @@ function resolveActiveOverride(field, watchedValues, fieldById) {
8536
8596
  }
8537
8597
  const depField = fieldById.get(parsed.field);
8538
8598
  const depKey = depField ? formKey(depField) : parsed.field;
8539
- if (String((_a = watchedValues[depKey]) != null ? _a : "") === parsed.value) {
8599
+ if (evaluateDependsOn(parsed, getWatchedValue(watchedValues, depKey))) {
8540
8600
  return ov;
8541
8601
  }
8542
8602
  }
@@ -8670,6 +8730,52 @@ function computeVirtualFieldValues(schema, existingValues) {
8670
8730
  }
8671
8731
 
8672
8732
  const SECURE_FIELD_CONFIGURED = "__CONFIGURED__";
8733
+ function resolveSecureKeyTemplate(template, item, index) {
8734
+ return template.replace(/\{index1\}/g, String(index + 1)).replace(/\{index\}/g, String(index)).replace(/\{item\.(\w+)\}/g, (_, key) => {
8735
+ var _a;
8736
+ return String((_a = item[key]) != null ? _a : "");
8737
+ });
8738
+ }
8739
+ function evaluateItemCondition(condition, item) {
8740
+ const trimmed = condition.trim();
8741
+ if (trimmed === "true") {
8742
+ return true;
8743
+ }
8744
+ const match = trimmed.match(/^item\.(\w+)\s*==\s*(.+)$/);
8745
+ if (!match) {
8746
+ return false;
8747
+ }
8748
+ const [, key, rawValue] = match;
8749
+ const actualValue = item[key];
8750
+ const expected = rawValue.trim();
8751
+ if (expected === "true") {
8752
+ return actualValue === true;
8753
+ }
8754
+ if (expected === "false") {
8755
+ return actualValue === false;
8756
+ }
8757
+ const unquoted = expected.replace(/^['"]|['"]$/g, "");
8758
+ return String(actualValue) === unquoted;
8759
+ }
8760
+ function findActiveSecureOverride(itemFields, item, index) {
8761
+ for (const field of itemFields) {
8762
+ if (!field.overrides) {
8763
+ continue;
8764
+ }
8765
+ for (const override of field.overrides) {
8766
+ if (!override.secureKey) {
8767
+ continue;
8768
+ }
8769
+ if (evaluateItemCondition(override.when, item)) {
8770
+ return {
8771
+ fieldKey: field.key,
8772
+ resolvedKey: resolveSecureKeyTemplate(override.secureKey, item, index)
8773
+ };
8774
+ }
8775
+ }
8776
+ }
8777
+ return null;
8778
+ }
8673
8779
  function patternToRegex(pattern) {
8674
8780
  const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8675
8781
  return new RegExp("^" + escaped.replace("\\{index\\}", "(\\d+)") + "$");
@@ -8776,7 +8882,7 @@ function resolveRawPath(ref, ds) {
8776
8882
  return void 0;
8777
8883
  }
8778
8884
  async function fetchExistingValues(dsUid, fields) {
8779
- var _a, _b, _c, _d, _e;
8885
+ var _a, _b, _c, _d, _e, _f;
8780
8886
  try {
8781
8887
  const ds = await runtime.getBackendSrv().get(`/api/datasources/uid/${dsUid}`, void 0, void 0, {
8782
8888
  showErrorAlert: false
@@ -8817,6 +8923,32 @@ async function fetchExistingValues(dsUid, fields) {
8817
8923
  }
8818
8924
  }
8819
8925
  }
8926
+ for (const field of fields) {
8927
+ if (field.valueType !== "array" || !((_f = field.item) == null ? void 0 : _f.fields)) {
8928
+ continue;
8929
+ }
8930
+ const itemFields = field.item.fields;
8931
+ const hasSecureOverride = itemFields.some((f) => {
8932
+ var _a2;
8933
+ return (_a2 = f.overrides) == null ? void 0 : _a2.some((o) => o.secureKey);
8934
+ });
8935
+ if (!hasSecureOverride) {
8936
+ continue;
8937
+ }
8938
+ const fk = formKey(field);
8939
+ const arr = values[fk];
8940
+ if (!Array.isArray(arr)) {
8941
+ continue;
8942
+ }
8943
+ values[fk] = arr.map((item, idx) => {
8944
+ var _a2;
8945
+ const match = findActiveSecureOverride(itemFields, item, idx);
8946
+ if (match && ((_a2 = ds.secureJsonFields) == null ? void 0 : _a2[match.resolvedKey])) {
8947
+ return { ...item, [match.fieldKey]: SECURE_FIELD_CONFIGURED };
8948
+ }
8949
+ return item;
8950
+ });
8951
+ }
8820
8952
  return { values, readOnly: !!ds.readOnly };
8821
8953
  } catch (err) {
8822
8954
  const message = err instanceof Error ? err.message : "Failed to load datasource configuration";
@@ -8824,7 +8956,7 @@ async function fetchExistingValues(dsUid, fields) {
8824
8956
  }
8825
8957
  }
8826
8958
  function buildDatasourceConfigPayload(data, fields, existing, isFieldVisible) {
8827
- var _a, _b, _c, _d;
8959
+ var _a, _b, _c, _d, _e, _f, _g, _h;
8828
8960
  const rootFields = {};
8829
8961
  const jsonData = { ...(_a = existing.jsonData) != null ? _a : {} };
8830
8962
  const secureJsonData = {};
@@ -8873,6 +9005,60 @@ function buildDatasourceConfigPayload(data, fields, existing, isFieldVisible) {
8873
9005
  break;
8874
9006
  }
8875
9007
  }
9008
+ for (const field of fields) {
9009
+ if (field.valueType !== "array" || !((_e = field.item) == null ? void 0 : _e.fields) || field.target !== "jsonData") {
9010
+ continue;
9011
+ }
9012
+ const itemFields = field.item.fields;
9013
+ const hasSecureOverride = itemFields.some((f) => {
9014
+ var _a2;
9015
+ return (_a2 = f.overrides) == null ? void 0 : _a2.some((o) => o.secureKey);
9016
+ });
9017
+ if (!hasSecureOverride) {
9018
+ continue;
9019
+ }
9020
+ const arr = field.section ? (_f = jsonData[field.section]) == null ? void 0 : _f[field.key] : jsonData[field.key];
9021
+ if (!Array.isArray(arr)) {
9022
+ continue;
9023
+ }
9024
+ const secureKeyOverrides = [];
9025
+ for (const f of itemFields) {
9026
+ for (const o of (_g = f.overrides) != null ? _g : []) {
9027
+ if (o.secureKey) {
9028
+ secureKeyOverrides.push({ fieldKey: f.key, override: o });
9029
+ }
9030
+ }
9031
+ }
9032
+ const activeSecureKeys = /* @__PURE__ */ new Set();
9033
+ for (let i = 0; i < arr.length; i++) {
9034
+ const item = arr[i];
9035
+ const match = findActiveSecureOverride(itemFields, item, i);
9036
+ if (!match) {
9037
+ continue;
9038
+ }
9039
+ const resolvedKey = match.resolvedKey;
9040
+ activeSecureKeys.add(resolvedKey);
9041
+ const itemValue = item[match.fieldKey];
9042
+ if (itemValue === SECURE_FIELD_CONFIGURED) {
9043
+ secureJsonFields[resolvedKey] = true;
9044
+ } else if (itemValue && itemValue !== "") {
9045
+ secureJsonData[resolvedKey] = String(itemValue);
9046
+ secureJsonFields[resolvedKey] = false;
9047
+ item[match.fieldKey] = "";
9048
+ }
9049
+ }
9050
+ const existingSecureFields = (_h = existing.secureJsonFields) != null ? _h : {};
9051
+ for (const existingKey of Object.keys(existingSecureFields)) {
9052
+ const couldBeFromThisField = secureKeyOverrides.some((sko) => {
9053
+ const staticPrefix = sko.override.secureKey.split("{")[0];
9054
+ return staticPrefix && existingKey.startsWith(staticPrefix);
9055
+ });
9056
+ if (couldBeFromThisField && !activeSecureKeys.has(existingKey)) {
9057
+ secureJsonFields[existingKey] = false;
9058
+ secureJsonData[existingKey] = "";
9059
+ }
9060
+ }
9061
+ }
8876
9062
  return { rootFields, jsonData, secureJsonData, secureJsonFields };
8877
9063
  }
8878
9064
  async function submitDatasourceConfig(dsUid, data, fields, isFieldVisible) {
@@ -8910,23 +9096,25 @@ function evaluateEffectCondition(when, fieldValue) {
8910
9096
  const eq = String(fieldValue) === String(target);
8911
9097
  return op === "==" ? eq : !eq;
8912
9098
  }
8913
- function isFieldRequired(field, watchedValues, fieldById) {
8914
- var _a;
9099
+ function isFieldRequired(field, watchedValues, fieldById, celContext) {
8915
9100
  if (field.required) {
8916
9101
  return true;
8917
9102
  }
8918
9103
  if (field.requiredWhen) {
9104
+ if (celContext) {
9105
+ return evaluateCelExpression(field.requiredWhen, celContext);
9106
+ }
8919
9107
  const parsed = parseDependsOn(field.requiredWhen);
8920
9108
  if (parsed) {
8921
9109
  const depField = fieldById.get(parsed.field);
8922
9110
  const depKey = depField ? formKey(depField) : parsed.field;
8923
- return String((_a = watchedValues[depKey]) != null ? _a : "") === parsed.value;
9111
+ return evaluateDependsOn(parsed, getWatchedValue(watchedValues, depKey));
8924
9112
  }
8925
9113
  }
8926
9114
  return false;
8927
9115
  }
8928
9116
  function buildValidationRules(field, required) {
8929
- var _a, _b, _c, _d, _e, _f;
9117
+ var _a, _b, _c, _d, _e, _f, _g, _h;
8930
9118
  const rules = {};
8931
9119
  const label = (_a = field.label) != null ? _a : field.key;
8932
9120
  if (required) {
@@ -8944,29 +9132,65 @@ function buildValidationRules(field, required) {
8944
9132
  rules.required = `${label} is required`;
8945
9133
  }
8946
9134
  }
9135
+ if (field.valueType === "array" && ((_b = field.item) == null ? void 0 : _b.valueType) === "object" && ((_c = field.item.fields) == null ? void 0 : _c.length)) {
9136
+ const requiredItemFields = field.item.fields.filter((f) => f.required);
9137
+ if (requiredItemFields.length > 0) {
9138
+ const existingValidate = rules.validate;
9139
+ rules.validate = (value) => {
9140
+ var _a2;
9141
+ if (existingValidate) {
9142
+ const r = existingValidate(value);
9143
+ if (r !== true) {
9144
+ return r;
9145
+ }
9146
+ }
9147
+ if (!Array.isArray(value)) {
9148
+ return true;
9149
+ }
9150
+ const itemErrors = {};
9151
+ for (let i = 0; i < value.length; i++) {
9152
+ const item = value[i];
9153
+ const missing = [];
9154
+ for (const rf of requiredItemFields) {
9155
+ const v = item[rf.key];
9156
+ if (v === void 0 || v === null || v === "") {
9157
+ missing.push((_a2 = rf.label) != null ? _a2 : rf.key);
9158
+ }
9159
+ }
9160
+ if (missing.length > 0) {
9161
+ itemErrors[i] = `${missing.join(", ")} required`;
9162
+ }
9163
+ }
9164
+ if (Object.keys(itemErrors).length === 0) {
9165
+ return true;
9166
+ }
9167
+ return `__ITEM_ERRORS__${JSON.stringify(itemErrors)}`;
9168
+ };
9169
+ }
9170
+ }
8947
9171
  if (field.validations) {
8948
9172
  for (const v of field.validations) {
8949
9173
  switch (v.type) {
8950
9174
  case "pattern":
8951
9175
  rules.pattern = {
8952
9176
  value: new RegExp(v.pattern),
8953
- message: (_b = v.message) != null ? _b : `Invalid ${label}`
9177
+ message: (_d = v.message) != null ? _d : `Invalid ${label}`
8954
9178
  };
8955
9179
  break;
8956
9180
  case "range":
8957
9181
  if (v.min !== void 0) {
8958
- rules.min = { value: v.min, message: (_c = v.message) != null ? _c : `Minimum value is ${v.min}` };
9182
+ rules.min = { value: v.min, message: (_e = v.message) != null ? _e : `Minimum value is ${v.min}` };
8959
9183
  }
8960
9184
  if (v.max !== void 0) {
8961
- rules.max = { value: v.max, message: (_d = v.message) != null ? _d : `Maximum value is ${v.max}` };
9185
+ rules.max = { value: v.max, message: (_f = v.message) != null ? _f : `Maximum value is ${v.max}` };
8962
9186
  }
8963
9187
  break;
8964
9188
  case "length":
8965
9189
  if (v.min !== void 0) {
8966
- rules.minLength = { value: v.min, message: (_e = v.message) != null ? _e : `Minimum length is ${v.min}` };
9190
+ rules.minLength = { value: v.min, message: (_g = v.message) != null ? _g : `Minimum length is ${v.min}` };
8967
9191
  }
8968
9192
  if (v.max !== void 0) {
8969
- rules.maxLength = { value: v.max, message: (_f = v.message) != null ? _f : `Maximum length is ${v.max}` };
9193
+ rules.maxLength = { value: v.max, message: (_h = v.message) != null ? _h : `Maximum length is ${v.max}` };
8970
9194
  }
8971
9195
  break;
8972
9196
  }
@@ -8974,16 +9198,24 @@ function buildValidationRules(field, required) {
8974
9198
  }
8975
9199
  return rules;
8976
9200
  }
9201
+ const ITEM_ERRORS_PREFIX = "__ITEM_ERRORS__";
9202
+ function parseItemErrors(errorMessage) {
9203
+ if (!errorMessage || !errorMessage.startsWith(ITEM_ERRORS_PREFIX)) {
9204
+ return null;
9205
+ }
9206
+ try {
9207
+ return JSON.parse(errorMessage.slice(ITEM_ERRORS_PREFIX.length));
9208
+ } catch (e) {
9209
+ return null;
9210
+ }
9211
+ }
8977
9212
 
8978
- function PdcFieldNote({
8979
- enabled,
8980
- description,
8981
- configUrl
8982
- }) {
8983
- const styles = ui.useStyles2(getStyles$f);
9213
+ const PdcFieldNote = (props) => {
9214
+ const { enabled, description, configUrl } = props;
9215
+ const styles = ui.useStyles2(getStyles$h);
8984
9216
  return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.wrapper }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.note }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "shield", size: "xs" }), /* @__PURE__ */ React__default.default.createElement("span", null, "Private data source connect: ", /* @__PURE__ */ React__default.default.createElement("strong", null, enabled ? "Enabled" : "Disabled")), description && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { content: description }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "info-circle", size: "xs" }))), /* @__PURE__ */ React__default.default.createElement(ui.LinkButton, { variant: "secondary", size: "sm", icon: "external-link-alt", href: configUrl }, "Configure in settings"));
8985
- }
8986
- const getStyles$f = (theme) => ({
9217
+ };
9218
+ const getStyles$h = (theme) => ({
8987
9219
  wrapper: css.css({
8988
9220
  display: "flex",
8989
9221
  alignItems: "center",
@@ -9003,18 +9235,14 @@ const getStyles$f = (theme) => ({
9003
9235
  })
9004
9236
  });
9005
9237
 
9006
- function SecureFieldInput({
9007
- formField,
9008
- placeholder,
9009
- disabled,
9010
- label
9011
- }) {
9238
+ function SecureFieldInput(props) {
9012
9239
  var _a;
9240
+ const { formField, placeholder, disabled } = props;
9013
9241
  const isConfigured = formField.value === SECURE_FIELD_CONFIGURED;
9014
9242
  const [revealed, setRevealed] = React.useState(false);
9015
9243
  const styles = ui.useStyles2(getSecureFieldStyles);
9016
9244
  if (isConfigured) {
9017
- return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.configuredRow }, /* @__PURE__ */ React__default.default.createElement("span", { className: styles.configuredBadge }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "lock", size: "xs" }), label, " configured"), !disabled && /* @__PURE__ */ React__default.default.createElement(ui.Button, { variant: "secondary", fill: "text", size: "sm", onClick: () => formField.onChange(""), type: "button" }, "Reset"));
9245
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.configuredRow }, /* @__PURE__ */ React__default.default.createElement("span", { className: styles.configuredBadge }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "lock", size: "xs" }), "Configured"), !disabled && /* @__PURE__ */ React__default.default.createElement(ui.Button, { variant: "secondary", fill: "text", size: "sm", onClick: () => formField.onChange(""), type: "button" }, "Reset"));
9018
9246
  }
9019
9247
  return /* @__PURE__ */ React__default.default.createElement(
9020
9248
  ui.Input,
@@ -9043,7 +9271,8 @@ const getSecureFieldStyles = (theme) => ({
9043
9271
  configuredRow: css.css({
9044
9272
  display: "flex",
9045
9273
  alignItems: "center",
9046
- gap: theme.spacing(1)
9274
+ gap: theme.spacing(1),
9275
+ whiteSpace: "nowrap"
9047
9276
  }),
9048
9277
  configuredBadge: css.css({
9049
9278
  display: "inline-flex",
@@ -9053,97 +9282,164 @@ const getSecureFieldStyles = (theme) => ({
9053
9282
  borderRadius: theme.shape.radius.pill,
9054
9283
  backgroundColor: theme.colors.success.transparent,
9055
9284
  color: theme.colors.success.text,
9056
- fontSize: theme.typography.bodySmall.fontSize
9285
+ fontSize: theme.typography.bodySmall.fontSize,
9286
+ whiteSpace: "nowrap"
9057
9287
  })
9058
9288
  });
9059
9289
 
9060
- function StringArrayInput({
9061
- value,
9062
- onChange,
9063
- placeholder,
9064
- disabled
9065
- }) {
9066
- const [draft, setDraft] = React.useState("");
9067
- const styles = ui.useStyles2(getStyles$e);
9068
- const addTag = () => {
9069
- const trimmed = draft.trim();
9070
- if (trimmed && !value.includes(trimmed)) {
9071
- onChange([...value, trimmed]);
9072
- }
9073
- setDraft("");
9074
- };
9075
- const removeTag = (tag) => {
9076
- onChange(value.filter((v) => v !== tag));
9077
- };
9078
- return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.container }, value.length > 0 && /* @__PURE__ */ React__default.default.createElement("div", { className: styles.tags }, value.map((tag) => /* @__PURE__ */ React__default.default.createElement("span", { key: tag, className: styles.tag }, tag, !disabled && /* @__PURE__ */ React__default.default.createElement(
9079
- "button",
9080
- {
9081
- type: "button",
9082
- className: styles.tagRemove,
9083
- onClick: () => removeTag(tag),
9084
- "aria-label": `Remove ${tag}`
9085
- },
9086
- /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "times", size: "xs" })
9087
- )))), /* @__PURE__ */ React__default.default.createElement(
9088
- ui.Input,
9089
- {
9090
- value: draft,
9091
- onChange: (e) => setDraft(e.currentTarget.value),
9092
- onKeyDown: (e) => {
9093
- if (e.key === "Enter") {
9094
- e.preventDefault();
9095
- addTag();
9096
- }
9097
- },
9098
- onBlur: addTag,
9099
- placeholder,
9100
- disabled
9101
- }
9102
- ));
9103
- }
9104
- const getStyles$e = (theme) => ({
9105
- container: css.css({
9106
- display: "flex",
9107
- flexDirection: "column",
9108
- gap: theme.spacing(0.5)
9109
- }),
9110
- tags: css.css({
9111
- display: "flex",
9112
- flexWrap: "wrap",
9113
- gap: theme.spacing(0.5)
9114
- }),
9115
- tag: css.css({
9290
+ const ComplexFieldNote = (props) => {
9291
+ const { count, label } = props;
9292
+ const styles = ui.useStyles2(getStyles$g);
9293
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.note }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "info-circle", size: "xs" }), /* @__PURE__ */ React__default.default.createElement("span", null, count > 0 ? `${count} ${label.toLowerCase()} configured.` : `No ${label.toLowerCase()} configured.`, " Configure in Grafana UI."));
9294
+ };
9295
+ const getStyles$g = (theme) => ({
9296
+ note: css.css({
9116
9297
  display: "inline-flex",
9117
9298
  alignItems: "center",
9118
- gap: theme.spacing(0.25),
9119
- padding: `${theme.spacing(0.25)} ${theme.spacing(0.75)}`,
9120
- borderRadius: theme.shape.radius.pill,
9121
- backgroundColor: theme.colors.background.secondary,
9122
- border: `1px solid ${theme.colors.border.weak}`,
9299
+ gap: theme.spacing(0.5),
9300
+ padding: `${theme.spacing(0.5)} ${theme.spacing(0.75)}`,
9123
9301
  fontSize: theme.typography.bodySmall.fontSize,
9124
- color: theme.colors.text.primary
9125
- }),
9126
- tagRemove: css.css({
9127
- display: "inline-flex",
9128
- alignItems: "center",
9129
- padding: 0,
9130
- border: "none",
9131
- backgroundColor: "transparent",
9132
- cursor: "pointer",
9133
9302
  color: theme.colors.text.secondary,
9134
- "&:hover": { color: theme.colors.text.primary }
9303
+ backgroundColor: theme.colors.background.secondary,
9304
+ borderRadius: theme.shape.radius.default,
9305
+ border: `1px solid ${theme.colors.border.weak}`
9135
9306
  })
9136
9307
  });
9137
9308
 
9138
- function IndexedPairRow({
9139
- item,
9140
- index,
9141
- disabled,
9142
- onUpdate,
9143
- onRemove,
9144
- hideControls
9145
- }) {
9146
- const styles = ui.useStyles2(getStyles$d);
9309
+ function FileUploadField(props) {
9310
+ var _a, _b, _c, _d, _e, _f, _g;
9311
+ const { field, formField, disabled, setValue } = props;
9312
+ const [mode, setModeLocal] = React.useState("upload");
9313
+ const [error, setError] = React.useState(null);
9314
+ const theme = ui.useTheme2();
9315
+ const mapping = (_a = field.ui) == null ? void 0 : _a.fileMapping;
9316
+ const accept = (_c = (_b = field.ui) == null ? void 0 : _b.accept) != null ? _c : [".json"];
9317
+ const setMode = React.useCallback(
9318
+ (next) => {
9319
+ setModeLocal(next);
9320
+ formField.onChange(next);
9321
+ },
9322
+ [formField]
9323
+ );
9324
+ const processJson = React.useCallback(
9325
+ (text) => {
9326
+ const fileMapping = mapping != null ? mapping : {};
9327
+ try {
9328
+ const parsed = JSON.parse(text);
9329
+ if (typeof parsed !== "object" || parsed === null) {
9330
+ setError("Invalid JSON object");
9331
+ return;
9332
+ }
9333
+ const requiredKeys = Object.keys(fileMapping);
9334
+ const missingKeys = requiredKeys.filter((k) => parsed[k] === void 0);
9335
+ if (missingKeys.length > 0) {
9336
+ setError(`Missing keys: ${missingKeys.join(", ")}`);
9337
+ return;
9338
+ }
9339
+ for (const [jsonKey, targetFieldId] of Object.entries(fileMapping)) {
9340
+ if (parsed[jsonKey] !== void 0) {
9341
+ const parts = targetFieldId.split(".");
9342
+ const fk = parts.length === 2 ? parts[1] : targetFieldId;
9343
+ setValue(fk, parsed[jsonKey]);
9344
+ }
9345
+ }
9346
+ setError(null);
9347
+ setModeLocal("manual");
9348
+ formField.onChange("manual");
9349
+ } catch (e) {
9350
+ setError("Invalid JSON");
9351
+ }
9352
+ },
9353
+ [mapping, setValue, formField]
9354
+ );
9355
+ const styles = getStyles$f(theme);
9356
+ if (mode === "manual") {
9357
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.buttonRow }, /* @__PURE__ */ React__default.default.createElement(ui.Button, { type: "button", fill: "outline", size: "sm", onClick: () => setMode("paste"), disabled }, "Paste JWT Token"), /* @__PURE__ */ React__default.default.createElement("span", { className: styles.separator }, "or"), /* @__PURE__ */ React__default.default.createElement(ui.Button, { type: "button", fill: "outline", size: "sm", onClick: () => setMode("upload"), disabled }, "Upload JWT Token"));
9358
+ }
9359
+ if (mode === "paste") {
9360
+ return /* @__PURE__ */ React__default.default.createElement("div", null, /* @__PURE__ */ React__default.default.createElement(
9361
+ ui.TextArea,
9362
+ {
9363
+ placeholder: (_e = (_d = field.ui) == null ? void 0 : _d.placeholder) != null ? _e : "Paste Google JWT token here",
9364
+ rows: (_g = (_f = field.ui) == null ? void 0 : _f.rows) != null ? _g : 10,
9365
+ onBlur: (e) => {
9366
+ const val = e.currentTarget.value;
9367
+ if (val.trim()) {
9368
+ processJson(val);
9369
+ }
9370
+ },
9371
+ disabled
9372
+ }
9373
+ ), /* @__PURE__ */ React__default.default.createElement("div", { className: styles.buttonRow }, /* @__PURE__ */ React__default.default.createElement(ui.Button, { type: "button", fill: "outline", size: "sm", onClick: () => setMode("upload"), disabled }, "Upload JWT Token"), /* @__PURE__ */ React__default.default.createElement("span", { className: styles.separator }, "or"), /* @__PURE__ */ React__default.default.createElement(ui.Button, { type: "button", fill: "outline", size: "sm", onClick: () => setMode("manual"), disabled }, "Fill In JWT Token manually")), error && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.error }, error));
9374
+ }
9375
+ return /* @__PURE__ */ React__default.default.createElement("div", null, /* @__PURE__ */ React__default.default.createElement(
9376
+ ui.FileDropzone,
9377
+ {
9378
+ options: { multiple: false, accept: accept.join(",") },
9379
+ readAs: "readAsText",
9380
+ onLoad: (result) => {
9381
+ if (typeof result === "string") {
9382
+ processJson(result);
9383
+ }
9384
+ }
9385
+ }
9386
+ ), /* @__PURE__ */ React__default.default.createElement("div", { className: styles.buttonRow }, /* @__PURE__ */ React__default.default.createElement(ui.Button, { type: "button", fill: "outline", size: "sm", onClick: () => setMode("paste"), disabled }, "Paste JWT Token"), /* @__PURE__ */ React__default.default.createElement("span", { className: styles.separator }, "or"), /* @__PURE__ */ React__default.default.createElement(ui.Button, { type: "button", fill: "outline", size: "sm", onClick: () => setMode("manual"), disabled }, "Fill In JWT Token manually")), error && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.error }, error));
9387
+ }
9388
+ function getStyles$f(theme) {
9389
+ return {
9390
+ buttonRow: css.css({
9391
+ marginTop: theme.spacing(1),
9392
+ display: "flex",
9393
+ alignItems: "center",
9394
+ gap: theme.spacing(0.5)
9395
+ }),
9396
+ separator: css.css({
9397
+ padding: `0 ${theme.spacing(0.5)}`,
9398
+ color: theme.colors.text.secondary
9399
+ }),
9400
+ error: css.css({
9401
+ color: theme.colors.error.text,
9402
+ fontSize: theme.typography.bodySmall.fontSize,
9403
+ marginTop: theme.spacing(0.5),
9404
+ display: "block"
9405
+ })
9406
+ };
9407
+ }
9408
+
9409
+ const IndexedPairEditor = (props) => {
9410
+ const { value, onChange, maxItems = 10, disabled, hideControls, itemLabel = "header" } = props;
9411
+ const styles = ui.useStyles2(getStyles$e);
9412
+ const items = Array.isArray(value) ? value : [];
9413
+ const addItem = () => {
9414
+ if (items.length >= maxItems) {
9415
+ return;
9416
+ }
9417
+ onChange([...items, { index: 0, name: "", value: "" }]);
9418
+ };
9419
+ const removeItem = (index) => {
9420
+ onChange(items.filter((_, i) => i !== index));
9421
+ };
9422
+ const updateItem = (index, key, val) => {
9423
+ const updated = items.map((item, i) => i === index ? { ...item, [key]: val } : item);
9424
+ onChange(updated);
9425
+ };
9426
+ return /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "column", gap: 0.5 }, items.map((item, i) => /* @__PURE__ */ React__default.default.createElement(
9427
+ IndexedPairRow,
9428
+ {
9429
+ key: item.index || `new-${i}`,
9430
+ item,
9431
+ index: i,
9432
+ disabled,
9433
+ onUpdate: updateItem,
9434
+ onRemove: removeItem,
9435
+ hideControls,
9436
+ itemLabel
9437
+ }
9438
+ )), !hideControls && items.length < maxItems && /* @__PURE__ */ React__default.default.createElement("div", null, /* @__PURE__ */ React__default.default.createElement(ui.Button, { variant: "secondary", size: "sm", icon: "plus", disabled, onClick: addItem, type: "button" }, "Add ", itemLabel)), !hideControls && items.length >= maxItems && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.limitNote }, "Maximum ", maxItems, " ", itemLabel, "s reached"));
9439
+ };
9440
+ const IndexedPairRow = (props) => {
9441
+ const { item, index, disabled, onUpdate, onRemove, hideControls, itemLabel = "header" } = props;
9442
+ const styles = ui.useStyles2(getStyles$e);
9147
9443
  const secureStyles = ui.useStyles2(getSecureFieldStyles);
9148
9444
  const isConfigured = item.value === SECURE_FIELD_CONFIGURED;
9149
9445
  const [editing, setEditing] = React.useState(false);
@@ -9152,9 +9448,14 @@ function IndexedPairRow({
9152
9448
  ui.Input,
9153
9449
  {
9154
9450
  value: item.name,
9155
- placeholder: "Header name",
9451
+ placeholder: "key",
9156
9452
  disabled: disabled || hideControls,
9157
- onChange: (e) => onUpdate(index, "name", e.currentTarget.value)
9453
+ onChange: (e) => onUpdate(index, "name", e.currentTarget.value),
9454
+ onKeyDown: (e) => {
9455
+ if (e.key === "Enter") {
9456
+ e.preventDefault();
9457
+ }
9458
+ }
9158
9459
  }
9159
9460
  )), /* @__PURE__ */ React__default.default.createElement("div", { className: styles.inputCell }, isConfigured && !editing ? /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "row", gap: 1, alignItems: "center" }, /* @__PURE__ */ React__default.default.createElement("span", { className: secureStyles.configuredBadge }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "lock", size: "xs" }), "Configured"), !disabled && /* @__PURE__ */ React__default.default.createElement(
9160
9461
  ui.Button,
@@ -9173,10 +9474,15 @@ function IndexedPairRow({
9173
9474
  ui.Input,
9174
9475
  {
9175
9476
  value: item.value === SECURE_FIELD_CONFIGURED ? "" : item.value,
9176
- placeholder: "Header value",
9477
+ placeholder: `${itemLabel} value`,
9177
9478
  disabled,
9178
9479
  type: revealed ? "text" : "password",
9179
9480
  onChange: (e) => onUpdate(index, "value", e.currentTarget.value),
9481
+ onKeyDown: (e) => {
9482
+ if (e.key === "Enter") {
9483
+ e.preventDefault();
9484
+ }
9485
+ },
9180
9486
  suffix: /* @__PURE__ */ React__default.default.createElement(
9181
9487
  ui.Button,
9182
9488
  {
@@ -9190,34 +9496,56 @@ function IndexedPairRow({
9190
9496
  }
9191
9497
  )
9192
9498
  }
9193
- )), !hideControls && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { content: "Remove header" }, /* @__PURE__ */ React__default.default.createElement(
9499
+ )), !hideControls && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { content: `Remove ${itemLabel}` }, /* @__PURE__ */ React__default.default.createElement(
9194
9500
  ui.Button,
9195
9501
  {
9196
9502
  variant: "secondary",
9197
9503
  fill: "text",
9198
9504
  size: "sm",
9199
9505
  icon: "trash-alt",
9200
- "aria-label": "Remove header",
9506
+ "aria-label": `Remove ${itemLabel}`,
9201
9507
  disabled,
9202
9508
  onClick: () => onRemove(index),
9203
9509
  type: "button"
9204
9510
  }
9205
9511
  )));
9206
- }
9207
- function IndexedPairEditor({
9208
- value,
9209
- onChange,
9210
- maxItems = 10,
9211
- disabled,
9212
- hideControls
9213
- }) {
9512
+ };
9513
+ const getStyles$e = (theme) => ({
9514
+ inputCell: css.css({
9515
+ flex: "1 1 0",
9516
+ minWidth: 0,
9517
+ overflow: "hidden"
9518
+ }),
9519
+ limitNote: css.css({
9520
+ fontSize: theme.typography.bodySmall.fontSize,
9521
+ color: theme.colors.text.secondary
9522
+ })
9523
+ });
9524
+
9525
+ function ObjectArrayEditor(props) {
9526
+ var _a, _b, _c, _d, _e, _f;
9527
+ const { field, value, onChange, disabled, errorMessage } = props;
9214
9528
  const styles = ui.useStyles2(getStyles$d);
9529
+ const itemFields = (_b = (_a = field.item) == null ? void 0 : _a.fields) != null ? _b : [];
9530
+ const layoutRows = computeLayoutRows(itemFields);
9215
9531
  const items = Array.isArray(value) ? value : [];
9532
+ const maxItems = (_e = (_d = (_c = field.validations) == null ? void 0 : _c.find((v) => v.type === "itemCount")) == null ? void 0 : _d.max) != null ? _e : 20;
9533
+ const label = (_f = field.label) != null ? _f : field.key;
9534
+ const itemErrors = parseItemErrors(errorMessage);
9216
9535
  const addItem = () => {
9536
+ var _a2, _b2;
9217
9537
  if (items.length >= maxItems) {
9218
9538
  return;
9219
9539
  }
9220
- onChange([...items, { index: 0, name: "", value: "" }]);
9540
+ const empty = {};
9541
+ for (const f of itemFields) {
9542
+ if (f.valueType === "boolean") {
9543
+ empty[f.key] = (_a2 = f.defaultValue) != null ? _a2 : false;
9544
+ } else {
9545
+ empty[f.key] = (_b2 = f.defaultValue) != null ? _b2 : "";
9546
+ }
9547
+ }
9548
+ onChange([...items, empty]);
9221
9549
  };
9222
9550
  const removeItem = (index) => {
9223
9551
  onChange(items.filter((_, i) => i !== index));
@@ -9226,78 +9554,241 @@ function IndexedPairEditor({
9226
9554
  const updated = items.map((item, i) => i === index ? { ...item, [key]: val } : item);
9227
9555
  onChange(updated);
9228
9556
  };
9229
- return /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "column", gap: 0.5 }, items.map((item, i) => /* @__PURE__ */ React__default.default.createElement(
9230
- IndexedPairRow,
9557
+ return /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "column", gap: 1 }, items.map((item, i) => /* @__PURE__ */ React__default.default.createElement(
9558
+ ObjectArrayItemRow,
9231
9559
  {
9232
- key: item.index || `new-${i}`,
9560
+ key: i,
9233
9561
  item,
9234
9562
  index: i,
9235
- disabled,
9563
+ itemFields,
9564
+ layoutRows,
9565
+ disabled: !!disabled,
9236
9566
  onUpdate: updateItem,
9237
9567
  onRemove: removeItem,
9238
- hideControls
9568
+ showLabels: true,
9569
+ error: itemErrors == null ? void 0 : itemErrors[i]
9570
+ }
9571
+ )), items.length < maxItems && /* @__PURE__ */ React__default.default.createElement("div", null, /* @__PURE__ */ React__default.default.createElement(ui.Button, { variant: "secondary", size: "sm", icon: "plus", disabled, onClick: addItem, type: "button" }, "Add ", label.toLowerCase())), items.length >= maxItems && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.limitNote }, "Maximum ", maxItems, " items reached"));
9572
+ }
9573
+ function computeLayoutRows(fields) {
9574
+ var _a;
9575
+ const hasExplicitRow = fields.some((f) => {
9576
+ var _a2;
9577
+ return ((_a2 = f.ui) == null ? void 0 : _a2.row) !== void 0;
9578
+ });
9579
+ if (!hasExplicitRow) {
9580
+ if (fields.length <= 3) {
9581
+ return [fields];
9582
+ }
9583
+ return fields.map((f) => [f]);
9584
+ }
9585
+ const rowMap = /* @__PURE__ */ new Map();
9586
+ const noRow = [];
9587
+ for (const f of fields) {
9588
+ if (((_a = f.ui) == null ? void 0 : _a.row) !== void 0) {
9589
+ const existing = rowMap.get(f.ui.row);
9590
+ if (existing) {
9591
+ existing.push(f);
9592
+ } else {
9593
+ rowMap.set(f.ui.row, [f]);
9594
+ }
9595
+ } else {
9596
+ noRow.push(f);
9239
9597
  }
9240
- )), !hideControls && items.length < maxItems && /* @__PURE__ */ React__default.default.createElement("div", null, /* @__PURE__ */ React__default.default.createElement(ui.Button, { variant: "secondary", size: "sm", icon: "plus", disabled, onClick: addItem, type: "button" }, "Add header")), !hideControls && items.length >= maxItems && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.limitNote }, "Maximum ", maxItems, " headers reached"));
9598
+ }
9599
+ const sortedKeys = [...rowMap.keys()].sort((a, b) => a - b);
9600
+ const rows = sortedKeys.map((k) => rowMap.get(k));
9601
+ for (const f of noRow) {
9602
+ rows.push([f]);
9603
+ }
9604
+ return rows;
9605
+ }
9606
+ function makeItemFormField(item, index, fieldKey, onUpdate) {
9607
+ return {
9608
+ name: `item-${index}-${fieldKey}`,
9609
+ value: item[fieldKey],
9610
+ onChange: (eventOrValue) => {
9611
+ if (eventOrValue != null && typeof eventOrValue === "object" && "target" in eventOrValue) {
9612
+ onUpdate(index, fieldKey, eventOrValue.target.value);
9613
+ } else {
9614
+ onUpdate(index, fieldKey, eventOrValue);
9615
+ }
9616
+ },
9617
+ onBlur: () => {
9618
+ }
9619
+ };
9620
+ }
9621
+ function effectiveItemField(field, isSecure) {
9622
+ if (!isSecure) {
9623
+ return field;
9624
+ }
9625
+ return { ...field, semanticType: "password" };
9626
+ }
9627
+ function ObjectArrayItemRow({
9628
+ item,
9629
+ index,
9630
+ itemFields,
9631
+ layoutRows,
9632
+ disabled,
9633
+ onUpdate,
9634
+ onRemove,
9635
+ showLabels,
9636
+ error
9637
+ }) {
9638
+ const styles = ui.useStyles2(getStyles$d);
9639
+ const secureOverride = findActiveSecureOverride(itemFields, item, index);
9640
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.itemRow }, layoutRows.map((row, rowIdx) => /* @__PURE__ */ React__default.default.createElement(ui.Stack, { key: rowIdx, direction: "row", gap: 1, alignItems: "flex-end", wrap: "wrap" }, row.map((field) => {
9641
+ var _a;
9642
+ const isSecureTarget = (secureOverride == null ? void 0 : secureOverride.fieldKey) === field.key;
9643
+ const eff = effectiveItemField(field, !!isSecureTarget);
9644
+ const formField = makeItemFormField(item, index, field.key, onUpdate);
9645
+ if (isSecureTarget && item[field.key] === SECURE_FIELD_CONFIGURED) {
9646
+ formField.value = SECURE_FIELD_CONFIGURED;
9647
+ }
9648
+ return /* @__PURE__ */ React__default.default.createElement("div", { key: field.key, className: field.valueType === "boolean" ? styles.boolCell : styles.inputCell }, /* @__PURE__ */ React__default.default.createElement("span", { className: styles.cellLabel }, (_a = field.label) != null ? _a : field.key, field.required && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.required }, " *")), renderFieldInput(eff, formField, disabled));
9649
+ }), rowIdx === 0 && /* @__PURE__ */ React__default.default.createElement("div", { className: styles.removeCellWithLabel }, /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { content: "Remove" }, /* @__PURE__ */ React__default.default.createElement(
9650
+ ui.Button,
9651
+ {
9652
+ variant: "secondary",
9653
+ fill: "text",
9654
+ size: "sm",
9655
+ icon: "trash-alt",
9656
+ "aria-label": "Remove item",
9657
+ disabled,
9658
+ onClick: () => onRemove(index),
9659
+ type: "button"
9660
+ }
9661
+ ))))), error && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.itemError }, error));
9241
9662
  }
9242
9663
  const getStyles$d = (theme) => ({
9664
+ itemRow: css.css({
9665
+ display: "flex",
9666
+ flexDirection: "column",
9667
+ gap: theme.spacing(0.5),
9668
+ padding: theme.spacing(1),
9669
+ borderRadius: theme.shape.radius.default,
9670
+ border: `1px solid ${theme.colors.border.weak}`
9671
+ }),
9243
9672
  inputCell: css.css({
9244
- flex: "1 1 0",
9245
- minWidth: 0,
9673
+ flex: "1 1 120px",
9674
+ minWidth: 120,
9246
9675
  overflow: "hidden"
9247
9676
  }),
9677
+ boolCell: css.css({
9678
+ flex: "0 0 auto",
9679
+ minWidth: 0
9680
+ }),
9681
+ cellLabel: css.css({
9682
+ fontSize: theme.typography.bodySmall.fontSize,
9683
+ color: theme.colors.text.secondary,
9684
+ marginBottom: theme.spacing(0.25)
9685
+ }),
9686
+ required: css.css({
9687
+ color: theme.colors.error.text
9688
+ }),
9689
+ removeCellWithLabel: css.css({
9690
+ flex: "0 0 auto",
9691
+ display: "flex",
9692
+ alignItems: "flex-end",
9693
+ paddingBottom: theme.spacing(0.25)
9694
+ }),
9695
+ itemError: css.css({
9696
+ fontSize: theme.typography.bodySmall.fontSize,
9697
+ color: theme.colors.error.text
9698
+ }),
9248
9699
  limitNote: css.css({
9249
9700
  fontSize: theme.typography.bodySmall.fontSize,
9250
9701
  color: theme.colors.text.secondary
9251
9702
  })
9252
9703
  });
9253
9704
 
9254
- function ComplexFieldNote({ count, label }) {
9705
+ const StringArrayInput = (props) => {
9706
+ const { value, onChange, placeholder, disabled, itemLabel = "item" } = props;
9255
9707
  const styles = ui.useStyles2(getStyles$c);
9256
- return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.note }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "info-circle", size: "xs" }), /* @__PURE__ */ React__default.default.createElement("span", null, count > 0 ? `${count} ${label.toLowerCase()} configured.` : `No ${label.toLowerCase()} configured.`, " Configure in Grafana UI."));
9257
- }
9708
+ const items = Array.isArray(value) ? value : [];
9709
+ const lastInputRef = React.useRef(null);
9710
+ const shouldFocusLastRef = React.useRef(false);
9711
+ React.useEffect(() => {
9712
+ if (shouldFocusLastRef.current && lastInputRef.current) {
9713
+ lastInputRef.current.focus();
9714
+ shouldFocusLastRef.current = false;
9715
+ }
9716
+ }, [items.length]);
9717
+ const addItem = () => {
9718
+ shouldFocusLastRef.current = true;
9719
+ onChange([...items, ""]);
9720
+ };
9721
+ const removeItem = (index) => {
9722
+ onChange(items.filter((_, i) => i !== index));
9723
+ };
9724
+ const updateItem = (index, val) => {
9725
+ onChange(items.map((item, i) => i === index ? val : item));
9726
+ };
9727
+ const isItemValid = (index, val) => {
9728
+ const trimmed = val.trim();
9729
+ if (trimmed === "") {
9730
+ return false;
9731
+ }
9732
+ return !items.some((item, i) => i !== index && item.trim() === trimmed);
9733
+ };
9734
+ const handleEnter = (index, currentValue) => {
9735
+ if (!isItemValid(index, currentValue)) {
9736
+ return;
9737
+ }
9738
+ if (index === items.length - 1) {
9739
+ shouldFocusLastRef.current = true;
9740
+ onChange([...items, ""]);
9741
+ }
9742
+ };
9743
+ return /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "column", gap: 0.5 }, items.map((item, i) => /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "row", gap: 1, key: i }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.inputCell }, /* @__PURE__ */ React__default.default.createElement(
9744
+ ui.Input,
9745
+ {
9746
+ ref: i === items.length - 1 ? lastInputRef : void 0,
9747
+ value: item,
9748
+ placeholder: placeholder != null ? placeholder : itemLabel,
9749
+ disabled,
9750
+ onChange: (e) => updateItem(i, e.currentTarget.value),
9751
+ onKeyDown: (e) => {
9752
+ if (e.key === "Enter") {
9753
+ e.preventDefault();
9754
+ handleEnter(i, e.currentTarget.value);
9755
+ }
9756
+ }
9757
+ }
9758
+ )), !disabled && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { content: `Remove ${itemLabel}` }, /* @__PURE__ */ React__default.default.createElement(
9759
+ ui.Button,
9760
+ {
9761
+ variant: "secondary",
9762
+ fill: "text",
9763
+ size: "sm",
9764
+ icon: "trash-alt",
9765
+ "aria-label": `Remove ${itemLabel}`,
9766
+ onClick: () => removeItem(i),
9767
+ type: "button"
9768
+ }
9769
+ )))), !disabled && /* @__PURE__ */ React__default.default.createElement("div", null, /* @__PURE__ */ React__default.default.createElement(ui.Button, { variant: "secondary", size: "sm", icon: "plus", onClick: addItem, type: "button" }, "Add")));
9770
+ };
9258
9771
  const getStyles$c = (theme) => ({
9259
- note: css.css({
9260
- display: "inline-flex",
9261
- alignItems: "center",
9262
- gap: theme.spacing(0.5),
9263
- padding: `${theme.spacing(0.5)} ${theme.spacing(0.75)}`,
9264
- fontSize: theme.typography.bodySmall.fontSize,
9265
- color: theme.colors.text.secondary,
9266
- backgroundColor: theme.colors.background.secondary,
9267
- borderRadius: theme.shape.radius.default,
9268
- border: `1px solid ${theme.colors.border.weak}`
9772
+ inputCell: css.css({
9773
+ flex: "1 1 0",
9774
+ minWidth: 0,
9775
+ overflow: "hidden"
9269
9776
  })
9270
9777
  });
9271
9778
 
9272
- function renderFieldInput(field, formField, disabled) {
9273
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
9779
+ function renderFieldInput(field, formField, disabled, errorMessage, setValue) {
9780
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
9274
9781
  const value = formField.value;
9275
9782
  const placeholder = (_a = field.ui) == null ? void 0 : _a.placeholder;
9276
9783
  const label = (_b = field.label) != null ? _b : field.key;
9784
+ if (((_c = field.ui) == null ? void 0 : _c.component) === "fileUpload" && field.ui.fileMapping && setValue) {
9785
+ return /* @__PURE__ */ React__default.default.createElement(FileUploadField, { field, formField, disabled, setValue });
9786
+ }
9277
9787
  if (field.target === "secureJsonData" || field.semanticType === "password" || field.semanticType === "token") {
9278
9788
  return /* @__PURE__ */ React__default.default.createElement(SecureFieldInput, { formField, placeholder: placeholder != null ? placeholder : label, disabled, label });
9279
9789
  }
9280
- if (field.valueType === "boolean") {
9281
- return /* @__PURE__ */ React__default.default.createElement(ui.Switch, { value: !!value, onChange: (e) => formField.onChange(e.currentTarget.checked), disabled });
9282
- }
9283
- if (field.valueType === "number") {
9284
- return /* @__PURE__ */ React__default.default.createElement(
9285
- ui.Input,
9286
- {
9287
- ...formField,
9288
- value: value !== void 0 && value !== null ? Number(value) : "",
9289
- type: "number",
9290
- placeholder,
9291
- disabled,
9292
- onChange: (e) => {
9293
- const n = e.currentTarget.valueAsNumber;
9294
- formField.onChange(Number.isNaN(n) ? void 0 : n);
9295
- }
9296
- }
9297
- );
9298
- }
9299
- if (((_c = field.ui) == null ? void 0 : _c.component) === "radio") {
9300
- const options = ((_e = (_d = field.ui) == null ? void 0 : _d.options) != null ? _e : []).map((opt) => ({
9790
+ if (((_d = field.ui) == null ? void 0 : _d.component) === "radio") {
9791
+ const options = ((_f = (_e = field.ui) == null ? void 0 : _e.options) != null ? _f : []).map((opt) => ({
9301
9792
  label: opt.label,
9302
9793
  value: String(opt.value),
9303
9794
  description: opt.description
@@ -9307,13 +9798,20 @@ function renderFieldInput(field, formField, disabled) {
9307
9798
  {
9308
9799
  options,
9309
9800
  value: String(value != null ? value : ""),
9310
- onChange: (v) => formField.onChange(v),
9801
+ onChange: (v) => {
9802
+ if (field.valueType === "number") {
9803
+ const n = Number(v);
9804
+ formField.onChange(Number.isNaN(n) ? v : n);
9805
+ } else {
9806
+ formField.onChange(v);
9807
+ }
9808
+ },
9311
9809
  disabled
9312
9810
  }
9313
9811
  ));
9314
9812
  }
9315
- if (((_f = field.ui) == null ? void 0 : _f.component) === "select") {
9316
- const options = ((_h = (_g = field.ui) == null ? void 0 : _g.options) != null ? _h : []).map((opt) => ({
9813
+ if (((_g = field.ui) == null ? void 0 : _g.component) === "select") {
9814
+ const options = ((_i = (_h = field.ui) == null ? void 0 : _h.options) != null ? _i : []).map((opt) => ({
9317
9815
  label: opt.label,
9318
9816
  value: String(opt.value),
9319
9817
  description: opt.description
@@ -9328,10 +9826,29 @@ function renderFieldInput(field, formField, disabled) {
9328
9826
  }
9329
9827
  );
9330
9828
  }
9829
+ if (field.valueType === "boolean") {
9830
+ return /* @__PURE__ */ React__default.default.createElement(ui.Switch, { value: !!value, onChange: (e) => formField.onChange(e.currentTarget.checked), disabled });
9831
+ }
9832
+ if (field.valueType === "number") {
9833
+ return /* @__PURE__ */ React__default.default.createElement(
9834
+ ui.Input,
9835
+ {
9836
+ ...formField,
9837
+ value: value !== void 0 && value !== null ? Number(value) : "",
9838
+ type: "number",
9839
+ placeholder,
9840
+ disabled,
9841
+ onChange: (e) => {
9842
+ const n = e.currentTarget.valueAsNumber;
9843
+ formField.onChange(Number.isNaN(n) ? void 0 : n);
9844
+ }
9845
+ }
9846
+ );
9847
+ }
9331
9848
  if (field.semanticType === "url") {
9332
9849
  return /* @__PURE__ */ React__default.default.createElement(ui.Input, { ...formField, value: String(value != null ? value : ""), type: "url", placeholder, disabled });
9333
9850
  }
9334
- if (((_i = field.ui) == null ? void 0 : _i.component) === "textarea") {
9851
+ if (((_j = field.ui) == null ? void 0 : _j.component) === "textarea") {
9335
9852
  return /* @__PURE__ */ React__default.default.createElement(
9336
9853
  ui.TextArea,
9337
9854
  {
@@ -9339,27 +9856,50 @@ function renderFieldInput(field, formField, disabled) {
9339
9856
  value: String(value != null ? value : ""),
9340
9857
  placeholder,
9341
9858
  disabled,
9342
- rows: (_k = (_j = field.ui) == null ? void 0 : _j.rows) != null ? _k : 3
9859
+ rows: (_l = (_k = field.ui) == null ? void 0 : _k.rows) != null ? _l : 3
9343
9860
  }
9344
9861
  );
9345
9862
  }
9346
- if (field.valueType === "array" && ((_l = field.item) == null ? void 0 : _l.valueType) === "string") {
9863
+ if (((_m = field.storage) == null ? void 0 : _m.type) === "indexedPair") {
9864
+ const items = Array.isArray(value) ? value : [];
9865
+ const maxItems = (_p = (_o = (_n = field.validations) == null ? void 0 : _n.find((v) => v.type === "itemCount")) == null ? void 0 : _o.max) != null ? _p : 10;
9866
+ const itemLabel = (_r = (_q = field.label) == null ? void 0 : _q.toLowerCase().replace(/s$/, "")) != null ? _r : "header";
9867
+ return /* @__PURE__ */ React__default.default.createElement(
9868
+ IndexedPairEditor,
9869
+ {
9870
+ value: items,
9871
+ onChange: formField.onChange,
9872
+ maxItems,
9873
+ disabled,
9874
+ itemLabel
9875
+ }
9876
+ );
9877
+ }
9878
+ if (field.valueType === "array" && ((_s = field.item) == null ? void 0 : _s.valueType) === "string") {
9347
9879
  return /* @__PURE__ */ React__default.default.createElement(
9348
9880
  StringArrayInput,
9349
9881
  {
9350
9882
  value: Array.isArray(value) ? value : [],
9351
9883
  onChange: formField.onChange,
9352
- placeholder: placeholder != null ? placeholder : `Add ${label.toLowerCase()}\u2026`,
9353
- disabled
9884
+ placeholder,
9885
+ disabled,
9886
+ itemLabel: label.toLowerCase().replace(/s$/, "")
9354
9887
  }
9355
9888
  );
9356
9889
  }
9357
- if (((_m = field.storage) == null ? void 0 : _m.type) === "indexedPair") {
9358
- const items = Array.isArray(value) ? value : [];
9359
- const maxItems = (_p = (_o = (_n = field.validations) == null ? void 0 : _n.find((v) => v.type === "itemCount")) == null ? void 0 : _o.max) != null ? _p : 10;
9360
- return /* @__PURE__ */ React__default.default.createElement(IndexedPairEditor, { value: items, onChange: formField.onChange, maxItems, disabled });
9890
+ if (field.valueType === "array" && ((_t = field.item) == null ? void 0 : _t.valueType) === "object" && ((_u = field.item.fields) == null ? void 0 : _u.length)) {
9891
+ return /* @__PURE__ */ React__default.default.createElement(
9892
+ ObjectArrayEditor,
9893
+ {
9894
+ field,
9895
+ value,
9896
+ onChange: formField.onChange,
9897
+ disabled,
9898
+ errorMessage
9899
+ }
9900
+ );
9361
9901
  }
9362
- if (field.valueType === "object" || field.valueType === "map" || field.valueType === "array" && (((_q = field.item) == null ? void 0 : _q.valueType) === "object" || ((_r = field.item) == null ? void 0 : _r.valueType) === "map")) {
9902
+ if (field.valueType === "object" || field.valueType === "map" || field.valueType === "array" && (((_v = field.item) == null ? void 0 : _v.valueType) === "object" || ((_w = field.item) == null ? void 0 : _w.valueType) === "map")) {
9363
9903
  const count = Array.isArray(value) ? value.length : 0;
9364
9904
  return /* @__PURE__ */ React__default.default.createElement(ComplexFieldNote, { count, label });
9365
9905
  }
@@ -9417,8 +9957,8 @@ const getWizardStyles = (theme) => ({
9417
9957
  color: theme.colors.text.primary
9418
9958
  }),
9419
9959
  stepCount: css.css({
9420
- fontSize: "11px",
9421
- fontWeight: "normal",
9960
+ fontSize: theme.typography.bodySmall.fontSize,
9961
+ fontWeight: theme.typography.fontWeightRegular,
9422
9962
  color: theme.colors.text.secondary
9423
9963
  }),
9424
9964
  navActions: css.css({
@@ -9481,12 +10021,22 @@ const getWizardStyles = (theme) => ({
9481
10021
  minWidth: 0
9482
10022
  }),
9483
10023
  fieldError: css.css({
9484
- fontSize: "11px",
10024
+ fontSize: theme.typography.bodySmall.fontSize,
9485
10025
  color: theme.colors.error.text
9486
10026
  })
9487
10027
  });
9488
10028
 
9489
- function SchemaField({ field, control, errors, disabled, dsUid, watchedValues, fieldById }) {
10029
+ function SchemaField({
10030
+ field,
10031
+ control,
10032
+ errors,
10033
+ disabled,
10034
+ dsUid,
10035
+ watchedValues,
10036
+ fieldById,
10037
+ celContext,
10038
+ setValue
10039
+ }) {
9490
10040
  const styles = ui.useStyles2(getWizardStyles);
9491
10041
  const activeOverride = resolveActiveOverride(field, watchedValues, fieldById);
9492
10042
  const fk = formKey(field);
@@ -9507,7 +10057,7 @@ function SchemaField({ field, control, errors, disabled, dsUid, watchedValues, f
9507
10057
  }
9508
10058
  );
9509
10059
  }
9510
- const required = isFieldRequired(field, watchedValues, fieldById);
10060
+ const required = isFieldRequired(field, watchedValues, fieldById, celContext);
9511
10061
  const validationRules = buildValidationRules(field, required);
9512
10062
  return /* @__PURE__ */ React__default.default.createElement(
9513
10063
  reactHookForm.Controller,
@@ -9522,6 +10072,7 @@ function SchemaField({ field, control, errors, disabled, dsUid, watchedValues, f
9522
10072
  const description = (_c = activeOverride == null ? void 0 : activeOverride.description) != null ? _c : field.description;
9523
10073
  const effectiveField = activeOverride ? {
9524
10074
  ...field,
10075
+ description,
9525
10076
  ui: {
9526
10077
  ...field.ui,
9527
10078
  placeholder: (_e = activeOverride.placeholder) != null ? _e : (_d = field.ui) == null ? void 0 : _d.placeholder
@@ -9529,18 +10080,14 @@ function SchemaField({ field, control, errors, disabled, dsUid, watchedValues, f
9529
10080
  } : field;
9530
10081
  const isReadOnly = !!(activeOverride == null ? void 0 : activeOverride.readOnly);
9531
10082
  const isBool = field.valueType === "boolean";
9532
- return /* @__PURE__ */ React__default.default.createElement("div", { className: isBool ? styles.fieldRowCenter : styles.fieldRow }, /* @__PURE__ */ React__default.default.createElement("div", { className: isBool ? styles.fieldLabelRowCompact : styles.fieldLabelRow }, /* @__PURE__ */ React__default.default.createElement("span", { className: styles.fieldLabel }, label, required && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.fieldRequired }, "*")), description && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { content: description }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "info-circle", size: "xs", className: styles.fieldInfoIcon }))), /* @__PURE__ */ React__default.default.createElement("div", { className: styles.fieldInputCol }, renderFieldInput(effectiveField, formField, disabled || isReadOnly), errorMessage && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.fieldError }, errorMessage)));
10083
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: isBool ? styles.fieldRowCenter : styles.fieldRow }, /* @__PURE__ */ React__default.default.createElement("div", { className: isBool ? styles.fieldLabelRowCompact : styles.fieldLabelRow }, /* @__PURE__ */ React__default.default.createElement("span", { className: styles.fieldLabel }, label, required && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.fieldRequired }, "*")), description && /* @__PURE__ */ React__default.default.createElement(ui.Tooltip, { content: description }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "info-circle", size: "xs", className: styles.fieldInfoIcon }))), /* @__PURE__ */ React__default.default.createElement("div", { className: styles.fieldInputCol }, renderFieldInput(effectiveField, formField, disabled || isReadOnly, errorMessage, setValue), errorMessage && !parseItemErrors(errorMessage) && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.fieldError }, errorMessage)));
9533
10084
  }
9534
10085
  }
9535
10086
  );
9536
10087
  }
9537
10088
 
9538
- function AuthorizationHeaderField({
9539
- headersFieldKey,
9540
- control,
9541
- disabled,
9542
- watchedValues
9543
- }) {
10089
+ const AuthorizationHeaderField = (props) => {
10090
+ const { headersFieldKey, control, disabled, watchedValues } = props;
9544
10091
  const styles = ui.useStyles2(getWizardStyles);
9545
10092
  const items = React.useMemo(
9546
10093
  () => Array.isArray(watchedValues[headersFieldKey]) ? watchedValues[headersFieldKey] : [],
@@ -9578,7 +10125,7 @@ function AuthorizationHeaderField({
9578
10125
  }
9579
10126
  }
9580
10127
  );
9581
- }
10128
+ };
9582
10129
 
9583
10130
  const getTabStyles = (theme) => ({
9584
10131
  root: css.css({
@@ -9628,7 +10175,7 @@ const getTabStyles = (theme) => ({
9628
10175
  width: 12,
9629
10176
  flexShrink: 0,
9630
10177
  alignSelf: "flex-start",
9631
- paddingTop: 4
10178
+ paddingTop: theme.spacing(0.5)
9632
10179
  }),
9633
10180
  navDot: css.css({
9634
10181
  width: 10,
@@ -9674,7 +10221,7 @@ const getTabStyles = (theme) => ({
9674
10221
  }),
9675
10222
  navLabel: css.css({
9676
10223
  fontSize: theme.typography.body.fontSize,
9677
- color: "rgba(204, 204, 220, 0.65)",
10224
+ color: theme.colors.text.secondary,
9678
10225
  lineHeight: 1.4
9679
10226
  }),
9680
10227
  navLabelActive: css.css({
@@ -9756,8 +10303,8 @@ const getTabStyles = (theme) => ({
9756
10303
  textAlign: "left"
9757
10304
  }),
9758
10305
  sectionBadge: css.css({
9759
- fontSize: "11px",
9760
- fontWeight: "normal",
10306
+ fontSize: theme.typography.bodySmall.fontSize,
10307
+ fontWeight: theme.typography.fontWeightRegular,
9761
10308
  color: theme.colors.text.disabled,
9762
10309
  border: `1px solid ${theme.colors.border.weak}`,
9763
10310
  borderRadius: theme.shape.radius.pill,
@@ -9875,15 +10422,18 @@ function TabLayout({ form, schema, dsUid, dsName, onRetest, healthError, renderA
9875
10422
  handleSubmit,
9876
10423
  errors,
9877
10424
  watchedValues,
10425
+ celContext,
9878
10426
  initializing,
9879
10427
  fetchError,
9880
10428
  submitting,
9881
10429
  submitError,
9882
10430
  readOnly,
9883
10431
  isFieldVisible,
10432
+ isFieldDisabled,
9884
10433
  isGroupValid,
9885
10434
  groupHasData,
9886
- onSubmit
10435
+ onSubmit,
10436
+ setValue
9887
10437
  } = form;
9888
10438
  const displayGroups = React.useMemo(() => resolvedGroups.filter((g) => g.group.id !== "_required"), [resolvedGroups]);
9889
10439
  const [expandedSections, setExpandedSections] = React.useState(() => {
@@ -10011,10 +10561,12 @@ function TabLayout({ form, schema, dsUid, dsName, onRetest, healthError, renderA
10011
10561
  field,
10012
10562
  control,
10013
10563
  errors,
10014
- disabled: allDisabled,
10564
+ disabled: allDisabled || isFieldDisabled(field),
10015
10565
  dsUid,
10016
10566
  watchedValues,
10017
- fieldById
10567
+ fieldById,
10568
+ celContext,
10569
+ setValue
10018
10570
  }
10019
10571
  )), showHeaders && /* @__PURE__ */ React__default.default.createElement(
10020
10572
  AuthorizationHeaderField,
@@ -10128,7 +10680,7 @@ function useDatasourceConfigForm({ schema, dsUid, onSuccess, onSaving }) {
10128
10680
  return;
10129
10681
  }
10130
10682
  for (const { sourceKey, effects } of fieldsWithEffects) {
10131
- const currentVal = watchedValues[sourceKey];
10683
+ const currentVal = getWatchedValue(watchedValues, sourceKey);
10132
10684
  const prevVal = prevEffectValuesRef.current[sourceKey];
10133
10685
  if (currentVal === prevVal) {
10134
10686
  continue;
@@ -10159,6 +10711,7 @@ function useDatasourceConfigForm({ schema, dsUid, onSuccess, onSaving }) {
10159
10711
  return {
10160
10712
  depKey: depField ? formKey(depField) : parsed.field,
10161
10713
  depValue: parsed.value,
10714
+ negate: parsed.negate,
10162
10715
  defaultValue: ov.defaultValue
10163
10716
  };
10164
10717
  }).filter(Boolean);
@@ -10176,42 +10729,70 @@ function useDatasourceConfigForm({ schema, dsUid, onSuccess, onSaving }) {
10176
10729
  }
10177
10730
  for (const { targetKey, fieldDefault, overrides } of fieldsWithOverrideDefaults) {
10178
10731
  for (const ov of overrides) {
10179
- const currentDepVal = String((_a = watchedValues[ov.depKey]) != null ? _a : "");
10732
+ const currentDepVal = String((_a = getWatchedValue(watchedValues, ov.depKey)) != null ? _a : "");
10180
10733
  const trackKey = `${targetKey}::${ov.depKey}`;
10181
10734
  const prevDepVal = prevOverrideDepRef.current[trackKey];
10182
10735
  if (currentDepVal === prevDepVal) {
10183
10736
  continue;
10184
10737
  }
10185
10738
  prevOverrideDepRef.current[trackKey] = currentDepVal;
10186
- if (currentDepVal === ov.depValue) {
10739
+ const active = evaluateDependsOn({ value: ov.depValue, negate: ov.negate }, currentDepVal);
10740
+ const wasActive = prevDepVal !== void 0 && evaluateDependsOn({ value: ov.depValue, negate: ov.negate }, prevDepVal);
10741
+ if (active) {
10187
10742
  setValue(targetKey, ov.defaultValue);
10188
- } else if (prevDepVal === ov.depValue && fieldDefault !== void 0) {
10743
+ } else if (wasActive && fieldDefault !== void 0) {
10189
10744
  setValue(targetKey, fieldDefault);
10190
10745
  }
10191
10746
  }
10192
10747
  }
10193
10748
  }, [watchedValues, fieldsWithOverrideDefaults, initializing, setValue]);
10749
+ const celContext = React.useMemo(() => {
10750
+ const ctx = {};
10751
+ for (const f of fieldById.values()) {
10752
+ const fk = formKey(f);
10753
+ const val = getWatchedValue(watchedValues, fk);
10754
+ if (val !== void 0) {
10755
+ const parts = f.id.split(".");
10756
+ let current = ctx;
10757
+ for (let i = 0; i < parts.length - 1; i++) {
10758
+ if (!(parts[i] in current) || typeof current[parts[i]] !== "object" || current[parts[i]] === null) {
10759
+ current[parts[i]] = {};
10760
+ }
10761
+ current = current[parts[i]];
10762
+ }
10763
+ current[parts[parts.length - 1]] = val;
10764
+ }
10765
+ }
10766
+ return ctx;
10767
+ }, [watchedValues, fieldById]);
10194
10768
  const isFieldVisible = React.useCallback(
10195
10769
  (field) => {
10196
- var _a, _b;
10197
- if (field.kind === "virtual" && !field.ui) {
10198
- return false;
10199
- }
10200
- if ((_a = field.tags) == null ? void 0 : _a.some((t) => t.startsWith("managed-by:"))) {
10201
- return false;
10202
- }
10203
- if (!field.dependsOn) {
10204
- return true;
10205
- }
10206
- const parsed = parseDependsOn(field.dependsOn);
10207
- if (!parsed) {
10208
- return true;
10770
+ function check(f, visited) {
10771
+ var _a;
10772
+ if (f.kind === "virtual" && !f.ui) {
10773
+ return false;
10774
+ }
10775
+ if ((_a = f.tags) == null ? void 0 : _a.some((t) => t.startsWith("managed-by:"))) {
10776
+ return false;
10777
+ }
10778
+ if (!f.dependsOn) {
10779
+ return true;
10780
+ }
10781
+ const refs = extractFieldRefs(f.dependsOn);
10782
+ for (const ref of refs) {
10783
+ const depField = fieldById.get(ref);
10784
+ if (depField && !visited.has(depField.id)) {
10785
+ visited.add(f.id);
10786
+ if (!check(depField, visited)) {
10787
+ return false;
10788
+ }
10789
+ }
10790
+ }
10791
+ return evaluateCelExpression(f.dependsOn, celContext);
10209
10792
  }
10210
- const depField = fieldById.get(parsed.field);
10211
- const depKey = depField ? formKey(depField) : parsed.field;
10212
- return String((_b = watchedValues[depKey]) != null ? _b : "") === parsed.value;
10793
+ return check(field, /* @__PURE__ */ new Set());
10213
10794
  },
10214
- [watchedValues, fieldById]
10795
+ [celContext, fieldById]
10215
10796
  );
10216
10797
  const onSubmit = React.useCallback(
10217
10798
  async (data) => {
@@ -10251,8 +10832,8 @@ function useDatasourceConfigForm({ schema, dsUid, onSuccess, onSaving }) {
10251
10832
  if (errors[formKey(field)]) {
10252
10833
  return false;
10253
10834
  }
10254
- if (isFieldRequired(field, watchedValues, fieldById)) {
10255
- const val = watchedValues[formKey(field)];
10835
+ if (isFieldRequired(field, watchedValues, fieldById, celContext)) {
10836
+ const val = getWatchedValue(watchedValues, formKey(field));
10256
10837
  if (val === SECURE_FIELD_CONFIGURED) {
10257
10838
  continue;
10258
10839
  }
@@ -10263,12 +10844,21 @@ function useDatasourceConfigForm({ schema, dsUid, onSuccess, onSaving }) {
10263
10844
  }
10264
10845
  return true;
10265
10846
  },
10266
- [errors, watchedValues, fieldById, isFieldVisible]
10847
+ [errors, watchedValues, fieldById, celContext, isFieldVisible]
10848
+ );
10849
+ const isFieldDisabled = React.useCallback(
10850
+ (field) => {
10851
+ if (!field.disabledWhen) {
10852
+ return false;
10853
+ }
10854
+ return evaluateCelExpression(field.disabledWhen, celContext);
10855
+ },
10856
+ [celContext]
10267
10857
  );
10268
10858
  const groupHasData = React.useCallback(
10269
10859
  (group) => {
10270
10860
  for (const field of group.fields) {
10271
- const val = watchedValues[formKey(field)];
10861
+ const val = getWatchedValue(watchedValues, formKey(field));
10272
10862
  if (val === void 0 || val === null || val === "" || val === false) {
10273
10863
  continue;
10274
10864
  }
@@ -10292,6 +10882,7 @@ function useDatasourceConfigForm({ schema, dsUid, onSuccess, onSaving }) {
10292
10882
  trigger,
10293
10883
  errors,
10294
10884
  watchedValues,
10885
+ celContext,
10295
10886
  // State
10296
10887
  initializing,
10297
10888
  fetchError,
@@ -10300,9 +10891,11 @@ function useDatasourceConfigForm({ schema, dsUid, onSuccess, onSaving }) {
10300
10891
  readOnly,
10301
10892
  // Callbacks
10302
10893
  isFieldVisible,
10894
+ isFieldDisabled,
10303
10895
  isGroupValid,
10304
10896
  groupHasData,
10305
- onSubmit
10897
+ onSubmit,
10898
+ setValue
10306
10899
  };
10307
10900
  }
10308
10901
 
@@ -10349,13 +10942,16 @@ function WizardLayout({ form, schema, dsUid, dsName, onRetest, healthError, rend
10349
10942
  trigger,
10350
10943
  errors,
10351
10944
  watchedValues,
10945
+ celContext,
10352
10946
  initializing,
10353
10947
  fetchError,
10354
10948
  submitting,
10355
10949
  submitError,
10356
10950
  readOnly,
10357
10951
  isFieldVisible,
10358
- onSubmit
10952
+ isFieldDisabled,
10953
+ onSubmit,
10954
+ setValue
10359
10955
  } = form;
10360
10956
  const arrowSteps = React.useMemo(() => {
10361
10957
  const requiredGroup = resolvedGroups.find((g) => g.group.id === "_required");
@@ -10401,8 +10997,8 @@ function WizardLayout({ form, schema, dsUid, dsName, onRetest, healthError, rend
10401
10997
  if (errors[formKey(field)]) {
10402
10998
  return false;
10403
10999
  }
10404
- if (isFieldRequired(field, watchedValues, fieldById)) {
10405
- const val = watchedValues[formKey(field)];
11000
+ if (isFieldRequired(field, watchedValues, fieldById, celContext)) {
11001
+ const val = getWatchedValue(watchedValues, formKey(field));
10406
11002
  if (val === SECURE_FIELD_CONFIGURED) {
10407
11003
  continue;
10408
11004
  }
@@ -10412,7 +11008,7 @@ function WizardLayout({ form, schema, dsUid, dsName, onRetest, healthError, rend
10412
11008
  }
10413
11009
  }
10414
11010
  return true;
10415
- }, [currentResolved, errors, visibleFieldsForStep, watchedValues, fieldById]);
11011
+ }, [currentResolved, errors, visibleFieldsForStep, watchedValues, fieldById, celContext]);
10416
11012
  const goNext = React.useCallback(async () => {
10417
11013
  if (!currentResolved) {
10418
11014
  return;
@@ -10506,10 +11102,12 @@ function WizardLayout({ form, schema, dsUid, dsName, onRetest, healthError, rend
10506
11102
  field,
10507
11103
  control,
10508
11104
  errors,
10509
- disabled: allDisabled,
11105
+ disabled: allDisabled || isFieldDisabled(field),
10510
11106
  dsUid,
10511
11107
  watchedValues,
10512
- fieldById
11108
+ fieldById,
11109
+ celContext,
11110
+ setValue
10513
11111
  }
10514
11112
  )), (currentResolved.group.id === "_required" || currentResolved.group.id === "auth") && httpHeadersField && /* @__PURE__ */ React__default.default.createElement(
10515
11113
  AuthorizationHeaderField,
@@ -12176,6 +12774,21 @@ function generateTroubleshootingFields(schema) {
12176
12774
  return result;
12177
12775
  }
12178
12776
 
12777
+ const ROOT_CONFIG_FIELDS = [
12778
+ "name",
12779
+ "type",
12780
+ "id",
12781
+ "uid",
12782
+ "access",
12783
+ "url",
12784
+ "basicAuth",
12785
+ "basicAuthUser",
12786
+ "user",
12787
+ "isDefault",
12788
+ "database",
12789
+ "withCredentials"
12790
+ ];
12791
+
12179
12792
  function yamlIndent(depth) {
12180
12793
  return " ".repeat(depth);
12181
12794
  }
@@ -12528,6 +13141,81 @@ function datasourceToProvisioningYaml(ds, schema) {
12528
13141
  lines.push(" # mySecret: xxxxxx");
12529
13142
  return lines.join("\n");
12530
13143
  }
13144
+ function getYamlRenderOptions(ds) {
13145
+ var _a, _b, _c;
13146
+ return {
13147
+ jsonDataKeys: Object.keys((_a = ds.jsonData) != null ? _a : {}).sort(),
13148
+ secureKeys: [
13149
+ .../* @__PURE__ */ new Set([
13150
+ ...Object.keys((_b = ds.secureJsonData) != null ? _b : {}),
13151
+ ...Object.keys((_c = ds.secureJsonFields) != null ? _c : {})
13152
+ ])
13153
+ ].sort()
13154
+ };
13155
+ }
13156
+ const configToYaml = (ds, options = {}) => {
13157
+ var _a, _b, _c;
13158
+ const { jsonDataKeys, secureKeys } = {
13159
+ ...getYamlRenderOptions(ds),
13160
+ ...options
13161
+ };
13162
+ const root = {};
13163
+ for (const key of ROOT_CONFIG_FIELDS) {
13164
+ root[key] = (_a = ds[key]) != null ? _a : "";
13165
+ }
13166
+ const jsonData = (_b = ds.jsonData) != null ? _b : {};
13167
+ if (jsonDataKeys.length > 0) {
13168
+ const jsonDataYaml = {};
13169
+ for (const key of jsonDataKeys) {
13170
+ jsonDataYaml[key] = (_c = jsonData[key]) != null ? _c : "";
13171
+ }
13172
+ root.jsonData = jsonDataYaml;
13173
+ }
13174
+ if (secureKeys.length > 0) {
13175
+ const secureJsonData = {};
13176
+ for (const key of secureKeys) {
13177
+ secureJsonData[key] = "********";
13178
+ }
13179
+ root.secureJsonData = secureJsonData;
13180
+ }
13181
+ return yaml__default.default.dump(root, { lineWidth: -1, sortKeys: false, noRefs: true }).trimEnd();
13182
+ };
13183
+ const formatYamlDiff = (existing, body) => {
13184
+ var _a, _b, _c, _d, _e, _f;
13185
+ const updated = { ...existing, ...body };
13186
+ const existingJD = (_a = existing.jsonData) != null ? _a : {};
13187
+ const updatedJD = (_b = updated.jsonData) != null ? _b : {};
13188
+ const jsonDataKeys = [.../* @__PURE__ */ new Set([...Object.keys(existingJD), ...Object.keys(updatedJD)])].sort();
13189
+ const secureKeys = [
13190
+ .../* @__PURE__ */ new Set([
13191
+ ...Object.keys((_c = existing.secureJsonData) != null ? _c : {}),
13192
+ ...Object.keys((_d = existing.secureJsonFields) != null ? _d : {}),
13193
+ ...Object.keys((_e = updated.secureJsonData) != null ? _e : {}),
13194
+ ...Object.keys((_f = updated.secureJsonFields) != null ? _f : {})
13195
+ ])
13196
+ ].sort();
13197
+ const currentYaml = configToYaml(existing, { jsonDataKeys, secureKeys });
13198
+ const updatedYaml = configToYaml(updated, { jsonDataKeys, secureKeys });
13199
+ const oldLines = currentYaml.split("\n");
13200
+ const newLines = updatedYaml.split("\n");
13201
+ const maxLen = Math.max(oldLines.length, newLines.length);
13202
+ const diffLines = [];
13203
+ for (let i = 0; i < maxLen; i++) {
13204
+ const oldLine = oldLines[i];
13205
+ const newLine = newLines[i];
13206
+ if (oldLine === newLine) {
13207
+ diffLines.push(` ${oldLine}`);
13208
+ continue;
13209
+ }
13210
+ if (oldLine !== void 0) {
13211
+ diffLines.push(`-${oldLine}`);
13212
+ }
13213
+ if (newLine !== void 0) {
13214
+ diffLines.push(`+${newLine}`);
13215
+ }
13216
+ }
13217
+ return "```diff\n" + diffLines.join("\n") + "\n```";
13218
+ };
12531
13219
 
12532
13220
  exports.getConfigSchema = schemas.getConfigSchema;
12533
13221
  exports.AccessoryButton = AccessoryButton;
@@ -12602,17 +13290,22 @@ exports.TableSelector = TableSelector;
12602
13290
  exports.TokenType = TokenType;
12603
13291
  exports.buildDatasourceConfigPayload = buildDatasourceConfigPayload;
12604
13292
  exports.computeVirtualFieldValues = computeVirtualFieldValues;
13293
+ exports.configToYaml = configToYaml;
12605
13294
  exports.convertLegacyAuthProps = convertLegacyAuthProps;
12606
13295
  exports.datasourceToProvisioningYaml = datasourceToProvisioningYaml;
12607
13296
  exports.evaluateComputedRead = evaluateComputedRead;
13297
+ exports.evaluateDependsOn = evaluateDependsOn;
12608
13298
  exports.expandIndexedPair = expandIndexedPair;
12609
13299
  exports.fetchExistingValues = fetchExistingValues;
13300
+ exports.findActiveSecureOverride = findActiveSecureOverride;
12610
13301
  exports.flattenIndexedPair = flattenIndexedPair;
12611
13302
  exports.formKey = formKey;
12612
13303
  exports.formatDate = formatDate;
13304
+ exports.formatYamlDiff = formatYamlDiff;
12613
13305
  exports.generateLLMHint = generateLLMHint;
12614
13306
  exports.generateTroubleshootingFields = generateTroubleshootingFields;
12615
13307
  exports.getStandardSQLCompletionProvider = getStandardSQLCompletionProvider;
13308
+ exports.getWatchedValue = getWatchedValue;
12616
13309
  exports.grafanaStandardSQLLanguage = language;
12617
13310
  exports.grafanaStandardSQLLanguageConf = conf;
12618
13311
  exports.parseDependsOn = parseDependsOn;
@@ -12620,6 +13313,7 @@ exports.resolveActiveOverride = resolveActiveOverride;
12620
13313
  exports.resolveGroups = resolveGroups;
12621
13314
  exports.resolvePluginName = resolvePluginName;
12622
13315
  exports.resolveRequiredFieldsGroup = resolveRequiredFieldsGroup;
13316
+ exports.resolveSecureKeyTemplate = resolveSecureKeyTemplate;
12623
13317
  exports.submitDatasourceConfig = submitDatasourceConfig;
12624
13318
  exports.toOption = toOption;
12625
13319
  exports.toProvisioningYaml = toProvisioningYaml;