@grafana/scenes 8.17.0 → 8.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4805,10 +4805,35 @@ SceneDataLayerSet.Component = ({ model }) => {
4805
4805
  return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, layers.map((layer) => /* @__PURE__ */ React__default.default.createElement(layer.Component, { model: layer, key: layer.state.key })));
4806
4806
  };
4807
4807
 
4808
+ function isSystemTransformationsProvider(o) {
4809
+ return typeof o === "object" && o !== null && "isSystemTransformationsProvider" in o;
4810
+ }
4811
+ function toSystemTransformation(transformation, position, origin) {
4812
+ if (typeof transformation === "function") {
4813
+ return { operator: transformation, topic: data.DataTopic.Series, origin, position };
4814
+ }
4815
+ return { ...transformation, origin, position };
4816
+ }
4817
+ function freezeResolved(resolved) {
4818
+ Object.freeze(resolved.prepend);
4819
+ Object.freeze(resolved.append);
4820
+ return Object.freeze(resolved);
4821
+ }
4822
+
4823
+ const NO_SERIES = [];
4824
+ const NO_SYSTEM_TRANSFORMATIONS = { prepend: [], append: [] };
4825
+ freezeResolved(NO_SYSTEM_TRANSFORMATIONS);
4826
+ Object.freeze(NO_SERIES);
4827
+
4808
4828
  class SceneDataTransformer extends SceneObjectBase {
4809
4829
  constructor(state) {
4810
4830
  super(state);
4811
4831
  this._results = new rxjs.ReplaySubject(1);
4832
+ /**
4833
+ * Whether a previous activation already ran a pass, which is what makes the provider found on this one a
4834
+ * *change* rather than a first discovery. Not carried over by clone, unlike `_prevDataFromSource`.
4835
+ */
4836
+ this._hasActivatedBefore = false;
4812
4837
  /**
4813
4838
  * Scan transformations for variable usage and re-process transforms when a variable values change
4814
4839
  */
@@ -4824,15 +4849,50 @@ class SceneDataTransformer extends SceneObjectBase {
4824
4849
  activationHandler() {
4825
4850
  const sourceData = this.getSourceData();
4826
4851
  this._subs.add(sourceData.subscribeToState((state) => this.transform(state.data)));
4852
+ const providerChanged = this._discoverProvider();
4827
4853
  if (sourceData.state.data) {
4828
- this.transform(sourceData.state.data);
4854
+ this.transform(sourceData.state.data, providerChanged);
4829
4855
  }
4856
+ this._hasActivatedBefore = true;
4830
4857
  return () => {
4831
4858
  if (this._transformSub) {
4832
4859
  this._transformSub.unsubscribe();
4833
4860
  }
4834
4861
  };
4835
4862
  }
4863
+ /**
4864
+ * Checks the parent for system transformation provider.
4865
+ * Reports whether what the pipeline should run has changed since the last pass, so the caller can force one.
4866
+ */
4867
+ _discoverProvider() {
4868
+ const previous = this._provider;
4869
+ const provider = this.parent;
4870
+ if (!isSystemTransformationsProvider(provider)) {
4871
+ this._provider = void 0;
4872
+ this._resolvedSystem = void 0;
4873
+ return this._hasActivatedBefore && previous !== void 0;
4874
+ }
4875
+ this._provider = provider;
4876
+ this._resolvedSystem = void 0;
4877
+ if (this._hasActivatedBefore && previous !== provider) {
4878
+ return true;
4879
+ }
4880
+ return this._systemChangedSinceLastPass();
4881
+ }
4882
+ /**
4883
+ * Whether the provider resolves to something other than what the last pass ran.
4884
+ *
4885
+ * The provider instance being unchanged is not enough to conclude the pipeline is unchanged: nothing
4886
+ * was watching while this was inactive, so a plugin that finished importing in that window resolves
4887
+ * differently now. Source frames usually survive a re-activation unchanged, so without this the pass
4888
+ * is skipped as already transformed and state.data keeps frames the reported pipeline never produced.
4889
+ */
4890
+ _systemChangedSinceLastPass() {
4891
+ if (!this._lastPassSystem) {
4892
+ return false;
4893
+ }
4894
+ return !lodash.isEqual(this._lastPassSystem, this.getResolvedSystemTransformations());
4895
+ }
4836
4896
  getSourceData() {
4837
4897
  if (this.state.$data) {
4838
4898
  if (this.state.$data instanceof SceneDataLayerSet) {
@@ -4857,9 +4917,79 @@ class SceneDataTransformer extends SceneObjectBase {
4857
4917
  }
4858
4918
  return true;
4859
4919
  }
4920
+ /**
4921
+ * @internal
4922
+ * Whether the pass behind the current state.data applied any system transformations. A provider that has
4923
+ * stopped contributing still needs one more pass to take them back out.
4924
+ */
4925
+ hasAppliedSystemTransformations() {
4926
+ const last = this._lastPassSystem;
4927
+ return last !== void 0 && (last.prepend.length > 0 || last.append.length > 0);
4928
+ }
4860
4929
  reprocessTransformations() {
4930
+ this._resolvedSystem = void 0;
4861
4931
  this.transform(this.getSourceData().state.data, true);
4862
4932
  }
4933
+ /**
4934
+ * The system transformations for the given source frames.
4935
+ * Public because provider output never reaches state, so this is the only way for readers
4936
+ * (the transformations editor, the inspect data tab) to see what the pipeline is running.
4937
+ */
4938
+ getResolvedSystemTransformations(series) {
4939
+ const provider = this._provider;
4940
+ if (!provider) {
4941
+ return NO_SYSTEM_TRANSFORMATIONS;
4942
+ }
4943
+ const frames = series != null ? series : this._sourceSeriesOrNone();
4944
+ const memo = this._resolvedSystem;
4945
+ if (memo && memo.series === frames) {
4946
+ return memo.resolved;
4947
+ }
4948
+ const { prepend = [], append = [] } = this._resolveProvider(provider, frames);
4949
+ const resolved = freezeResolved({
4950
+ prepend: prepend.map((t) => toSystemTransformation(t, "prepend", provider.origin)),
4951
+ append: append.map((t) => toSystemTransformation(t, "append", provider.origin))
4952
+ });
4953
+ this._resolvedSystem = { series: frames, resolved };
4954
+ return resolved;
4955
+ }
4956
+ /**
4957
+ * The source frames to resolve a provider against when the caller did not supply their own.
4958
+ *
4959
+ * Mirrors getSourceData's precondition: reading what is running must not throw for a transformer that is not wired.
4960
+ * Since every VizPanel is a provider, a panel without `$data` would throw.
4961
+ */
4962
+ _sourceSeriesOrNone() {
4963
+ var _a, _b, _c;
4964
+ if (!this.state.$data && !((_a = this.parent) == null ? void 0 : _a.parent)) {
4965
+ return NO_SERIES;
4966
+ }
4967
+ return (_c = (_b = this.getSourceData().state.data) == null ? void 0 : _b.series) != null ? _c : NO_SERIES;
4968
+ }
4969
+ _resolveProvider(provider, series) {
4970
+ var _a;
4971
+ try {
4972
+ return (_a = provider.getSystemTransformations({ series })) != null ? _a : {};
4973
+ } catch (err) {
4974
+ console.error(`Error resolving system transformations for origin '${provider.origin}': `, err);
4975
+ return {};
4976
+ }
4977
+ }
4978
+ /**
4979
+ * The system transformations to run for the given source frames.
4980
+ */
4981
+ _systemTransformationsFor(series) {
4982
+ return this._provider ? this.getResolvedSystemTransformations(series) : NO_SYSTEM_TRANSFORMATIONS;
4983
+ }
4984
+ /**
4985
+ * Places the user configured transformations between the system tiers, in prepend, user, append order.
4986
+ */
4987
+ _withSystemTransformations(system, transformations) {
4988
+ if (system.prepend.length === 0 && system.append.length === 0) {
4989
+ return transformations;
4990
+ }
4991
+ return [...system.prepend, ...transformations, ...system.append];
4992
+ }
4863
4993
  /**
4864
4994
  * S3.1: Calculate transformation complexity metrics
4865
4995
  */
@@ -4894,6 +5024,7 @@ class SceneDataTransformer extends SceneObjectBase {
4894
5024
  const clone = super.clone(withState);
4895
5025
  if (this._prevDataFromSource) {
4896
5026
  clone["_prevDataFromSource"] = this._prevDataFromSource;
5027
+ clone["_lastPassSystem"] = this._lastPassSystem;
4897
5028
  }
4898
5029
  return clone;
4899
5030
  }
@@ -4933,15 +5064,21 @@ class SceneDataTransformer extends SceneObjectBase {
4933
5064
  return false;
4934
5065
  }
4935
5066
  transform(data$1, force = false) {
4936
- var _a;
5067
+ var _a, _b;
4937
5068
  const timestamp = performance.now();
4938
5069
  const profiler = findPanelProfiler(this);
4939
5070
  const transformStartTime = performance.now();
4940
5071
  let transformationId;
4941
5072
  let endTransformCallback = null;
4942
- if (this.state.transformations.length === 0 || !data$1) {
5073
+ const system = data$1 ? this._systemTransformationsFor(data$1.series) : NO_SYSTEM_TRANSFORMATIONS;
5074
+ const transformations = data$1 ? this._withSystemTransformations(system, this.state.transformations) : [];
5075
+ if (transformations.length === 0 || !data$1) {
5076
+ (_a = this._transformSub) == null ? void 0 : _a.unsubscribe();
4943
5077
  this._prevDataFromSource = data$1;
4944
- this.setState({ data: data$1 });
5078
+ this._lastPassSystem = system;
5079
+ if (data$1 !== this.state.data) {
5080
+ this.setState({ data: data$1 });
5081
+ }
4945
5082
  if (data$1) {
4946
5083
  this._results.next({ origin: this, data: data$1 });
4947
5084
  }
@@ -4951,7 +5088,7 @@ class SceneDataTransformer extends SceneObjectBase {
4951
5088
  return;
4952
5089
  }
4953
5090
  if (profiler) {
4954
- const transformationTypes = this.state.transformations.map((t) => {
5091
+ const transformationTypes = transformations.map((t) => {
4955
5092
  if ("id" in t) {
4956
5093
  return t.id;
4957
5094
  } else {
@@ -4959,10 +5096,13 @@ class SceneDataTransformer extends SceneObjectBase {
4959
5096
  }
4960
5097
  }).join("+");
4961
5098
  transformationId = transformationTypes || "no-transforms";
4962
- const metrics = this._calculateTransformationMetrics(data$1, this.state.transformations);
5099
+ const metrics = this._calculateTransformationMetrics(data$1, transformations);
4963
5100
  endTransformCallback = profiler.onDataTransformStart(timestamp, transformationId, metrics);
4964
5101
  }
4965
- const interpolatedTransformations = this._interpolateVariablesInTransformationConfigs(data$1);
5102
+ const interpolatedTransformations = this._withSystemTransformations(
5103
+ system,
5104
+ this._interpolateVariablesInTransformationConfigs(data$1, this.state.transformations)
5105
+ );
4966
5106
  const seriesTransformations = this._filterAndPrepareTransformationsByTopic(
4967
5107
  interpolatedTransformations,
4968
5108
  (transformation) => {
@@ -4991,7 +5131,7 @@ class SceneDataTransformer extends SceneObjectBase {
4991
5131
  }
4992
5132
  };
4993
5133
  const seriesStream = data.transformDataFrame(seriesTransformations, data$1.series, ctx);
4994
- const annotationsStream = data.transformDataFrame(annotationsTransformations, (_a = data$1.annotations) != null ? _a : []);
5134
+ const annotationsStream = data.transformDataFrame(annotationsTransformations, (_b = data$1.annotations) != null ? _b : []);
4995
5135
  let series = [];
4996
5136
  let annotations = [];
4997
5137
  this._transformSub = rxjs.forkJoin([seriesStream, annotationsStream]).pipe(
@@ -5042,21 +5182,21 @@ class SceneDataTransformer extends SceneObjectBase {
5042
5182
  this.setState({ data: transformedData });
5043
5183
  this._results.next({ origin: this, data: transformedData });
5044
5184
  this._prevDataFromSource = data$1;
5185
+ this._lastPassSystem = system;
5045
5186
  });
5046
5187
  }
5047
- _interpolateVariablesInTransformationConfigs(data) {
5188
+ _interpolateVariablesInTransformationConfigs(data, transformations) {
5048
5189
  var _a;
5049
- const transformations = this.state.transformations;
5050
5190
  if (this._variableDependency.getNames().size === 0) {
5051
5191
  return transformations;
5052
5192
  }
5053
- const onlyObjects = transformations.every((t) => typeof t === "object");
5054
- if (onlyObjects) {
5193
+ const isInterpolatable = (t) => typeof t === "object" && !("operator" in t);
5194
+ if (transformations.every(isInterpolatable)) {
5055
5195
  return JSON.parse(sceneGraph.interpolate(this, JSON.stringify(transformations), (_a = data.request) == null ? void 0 : _a.scopedVars));
5056
5196
  }
5057
5197
  return transformations.map((t) => {
5058
5198
  var _a2;
5059
- return typeof t === "object" ? JSON.parse(sceneGraph.interpolate(this, JSON.stringify(t), (_a2 = data.request) == null ? void 0 : _a2.scopedVars)) : t;
5199
+ return isInterpolatable(t) ? JSON.parse(sceneGraph.interpolate(this, JSON.stringify(t), (_a2 = data.request) == null ? void 0 : _a2.scopedVars)) : t;
5060
5200
  });
5061
5201
  }
5062
5202
  _filterAndPrepareTransformationsByTopic(interpolatedTransformations, transformationFilter) {
@@ -5083,6 +5223,16 @@ class VizPanel extends SceneObjectBase {
5083
5223
  }
5084
5224
  });
5085
5225
  this._structureRev = 0;
5226
+ /**
5227
+ * @internal
5228
+ * Marks this panel as a SystemTransformationsProvider; not for app code.
5229
+ */
5230
+ this.isSystemTransformationsProvider = true;
5231
+ /**
5232
+ * @internal
5233
+ * Identifies this panel's tier in its data pipeline; not for app code.
5234
+ */
5235
+ this.origin = "plugin";
5086
5236
  this.onTimeRangeChange = (timeRange) => {
5087
5237
  const sceneTimeRange = sceneGraph.getTimeRange(this);
5088
5238
  sceneTimeRange.onTimeRangeChange({
@@ -5271,16 +5421,9 @@ class VizPanel extends SceneObjectBase {
5271
5421
  endPluginLoadCallback == null ? void 0 : endPluginLoadCallback(plugin, true);
5272
5422
  this._pluginLoaded(plugin, overwriteOptions, overwriteFieldConfig, isAfterPluginChange);
5273
5423
  } else {
5274
- const { importPanelPlugin } = runtime.getPluginImportUtils();
5275
5424
  try {
5276
5425
  const endPluginLoadCallback = profiler == null ? void 0 : profiler.onPluginLoadStart(pluginId);
5277
- const panelPromise = importPanelPlugin(pluginId);
5278
- const queryControler = sceneGraph.getQueryController(this);
5279
- if (queryControler && queryControler.state.enableProfiling) {
5280
- wrapPromiseInStateObservable(panelPromise).pipe(registerQueryWithController({ type: `VizPanel/loadPlugin/${pluginId}`, origin: this })).subscribe(() => {
5281
- });
5282
- }
5283
- const result = await panelPromise;
5426
+ const result = await this._importPlugin(pluginId);
5284
5427
  endPluginLoadCallback == null ? void 0 : endPluginLoadCallback(result, false);
5285
5428
  this._pluginLoaded(result, overwriteOptions, overwriteFieldConfig, isAfterPluginChange);
5286
5429
  } catch (err) {
@@ -5291,6 +5434,24 @@ class VizPanel extends SceneObjectBase {
5291
5434
  }
5292
5435
  }
5293
5436
  }
5437
+ /**
5438
+ * The only place the plugin is imported. Concurrent callers (_loadPlugin, getPluginAsync) share one import,
5439
+ * and a rejection stays cached so a plugin that fails to load is not retried on every call.
5440
+ */
5441
+ _importPlugin(pluginId) {
5442
+ var _a;
5443
+ if (((_a = this._pluginImport) == null ? void 0 : _a.pluginId) === pluginId) {
5444
+ return this._pluginImport.promise;
5445
+ }
5446
+ const promise = runtime.getPluginImportUtils().importPanelPlugin(pluginId);
5447
+ const queryControler = sceneGraph.getQueryController(this);
5448
+ if (queryControler && queryControler.state.enableProfiling) {
5449
+ wrapPromiseInStateObservable(promise).pipe(registerQueryWithController({ type: `VizPanel/loadPlugin/${pluginId}`, origin: this })).subscribe(() => {
5450
+ });
5451
+ }
5452
+ this._pluginImport = { pluginId, promise };
5453
+ return promise;
5454
+ }
5294
5455
  getLegacyPanelId() {
5295
5456
  var _a, _b;
5296
5457
  const parts = (_b = (_a = this.state.key) == null ? void 0 : _a.split("/")) != null ? _b : [];
@@ -5359,6 +5520,11 @@ class VizPanel extends SceneObjectBase {
5359
5520
  pluginVersion: currentVersion,
5360
5521
  pluginId: plugin.meta.id
5361
5522
  });
5523
+ if (this.state.applyPluginTransformations && // does panel support system transformations?
5524
+ $data instanceof SceneDataTransformer && $data.isActive && // the plugin supplies a provider method, or a previous one did and its transformations must come back out
5525
+ (pluginProvidesSystemTransformations(plugin) || $data.hasAppliedSystemTransformations())) {
5526
+ $data.reprocessTransformations();
5527
+ }
5362
5528
  if (plugin.meta.skipDataQuery) {
5363
5529
  const sceneTimeRange = sceneGraph.getTimeRange(this);
5364
5530
  this._subs.add(sceneTimeRange.subscribeToState(() => this.forceRender()));
@@ -5367,9 +5533,50 @@ class VizPanel extends SceneObjectBase {
5367
5533
  _getPluginVersion(plugin) {
5368
5534
  return plugin && plugin.meta.info.version ? plugin.meta.info.version : runtime.config.buildInfo.version;
5369
5535
  }
5536
+ /**
5537
+ * The plugin this panel has adopted, or undefined while it is still loading.
5538
+ */
5370
5539
  getPlugin() {
5371
5540
  return this._plugin;
5372
5541
  }
5542
+ /**
5543
+ * The plugin for the current state.pluginId, importing it if it is not loaded yet. Rejects when
5544
+ * the import does, like the host's importPanelPlugin.
5545
+ *
5546
+ * Unlike getPlugin this does not return undefined merely because the import has not finished, and
5547
+ * it does not wait on the panel being activated. It does not adopt what it resolves: option
5548
+ * migrations and the state write stay with _loadPlugin, on the render and viz change paths.
5549
+ */
5550
+ getPluginAsync() {
5551
+ const resolved = this._resolvePluginSync();
5552
+ return resolved ? Promise.resolve(resolved) : this._importPlugin(this.state.pluginId);
5553
+ }
5554
+ /**
5555
+ * The plugin for the current state.pluginId when it is available without importing: the one this
5556
+ * panel adopted, or one already in the host's cache.
5557
+ */
5558
+ _resolvePluginSync() {
5559
+ var _a;
5560
+ const { pluginId } = this.state;
5561
+ return ((_a = this._plugin) == null ? void 0 : _a.meta.id) === pluginId ? this._plugin : loadPanelPluginSync(pluginId);
5562
+ }
5563
+ /**
5564
+ * @internal
5565
+ * Called by the SceneDataTransformer that discovered this panel with the frames about to enter the pipeline.
5566
+ */
5567
+ getSystemTransformations({ series }) {
5568
+ if (series.length === 0) {
5569
+ return {};
5570
+ }
5571
+ const plugin = this._pluginForTransformations();
5572
+ return plugin ? getPluginSystemTransformations(plugin, { series }) : {};
5573
+ }
5574
+ /**
5575
+ * The plugin that contributes the transformations for this panel.
5576
+ */
5577
+ _pluginForTransformations() {
5578
+ return this.state.applyPluginTransformations ? this._resolvePluginSync() : void 0;
5579
+ }
5373
5580
  getPanelContext() {
5374
5581
  var _a;
5375
5582
  (_a = this._panelContext) != null ? _a : this._panelContext = this.buildPanelContext();
@@ -5523,6 +5730,23 @@ function getPanelPluginNotFound(id) {
5523
5730
  };
5524
5731
  return plugin;
5525
5732
  }
5733
+ function pluginProvidesSystemTransformations(plugin) {
5734
+ return typeof plugin.getSystemTransformations === "function";
5735
+ }
5736
+ function getPluginSystemTransformations(plugin, ctx) {
5737
+ var _a;
5738
+ if (!pluginProvidesSystemTransformations(plugin)) {
5739
+ return {};
5740
+ }
5741
+ const { prepend = [], append = [] } = (_a = plugin.getSystemTransformations(ctx)) != null ? _a : {};
5742
+ return { prepend: prepend.filter(appliesToSeriesTopic), append: append.filter(appliesToSeriesTopic) };
5743
+ }
5744
+ function appliesToSeriesTopic(transformation) {
5745
+ if (typeof transformation === "function") {
5746
+ return true;
5747
+ }
5748
+ return transformation.topic == null || transformation.topic === data.DataTopic.Series;
5749
+ }
5526
5750
 
5527
5751
  function findPanelProfiler(sceneObject) {
5528
5752
  try {
@@ -19071,6 +19295,7 @@ exports.isDataRequestEnricher = isDataRequestEnricher;
19071
19295
  exports.isFiltersRequestEnricher = isFiltersRequestEnricher;
19072
19296
  exports.isGroupByFilter = isGroupByFilter;
19073
19297
  exports.isSceneObject = isSceneObject;
19298
+ exports.isSystemTransformationsProvider = isSystemTransformationsProvider;
19074
19299
  exports.loadResources = loadResources;
19075
19300
  exports.performanceUtils = index$1;
19076
19301
  exports.registerQueryWithController = registerQueryWithController;