@grafana/scenes 5.7.4--canary.858.10213075790.0 → 5.7.4

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafana/scenes",
3
- "version": "5.7.4--canary.858.10213075790.0",
3
+ "version": "5.7.4",
4
4
  "description": "Grafana framework for building dynamic dashboards",
5
5
  "author": "Grafana Labs",
6
6
  "license": "AGPL-3.0-only",
@@ -113,5 +113,5 @@
113
113
  "prettier --write"
114
114
  ]
115
115
  },
116
- "gitHead": "4642e174ae536666c0d6aef76c8237ec393ebcde"
116
+ "gitHead": "c32882c8b4d0f6fad2dde39e5ab8e5822a989372"
117
117
  }
@@ -1,136 +0,0 @@
1
- import { writeSceneLog } from '../utils/writeSceneLog.js';
2
-
3
- var __accessCheck = (obj, member, msg) => {
4
- if (!member.has(obj))
5
- throw TypeError("Cannot " + msg);
6
- };
7
- var __privateGet = (obj, member, getter) => {
8
- __accessCheck(obj, member, "read from private field");
9
- return getter ? getter.call(obj) : member.get(obj);
10
- };
11
- var __privateAdd = (obj, member, value) => {
12
- if (member.has(obj))
13
- throw TypeError("Cannot add the same private member more than once");
14
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
15
- };
16
- var __privateSet = (obj, member, value, setter) => {
17
- __accessCheck(obj, member, "write to private field");
18
- setter ? setter.call(obj, value) : member.set(obj, value);
19
- return value;
20
- };
21
- var _profileInProgress, _profileStartTs, _trailAnimationFrameId, _recordedTrailingSpans;
22
- const POST_STORM_WINDOW = 2e3;
23
- const SPAN_THRESHOLD = 30;
24
- class SceneRenderProfiler {
25
- constructor(queryController) {
26
- this.queryController = queryController;
27
- __privateAdd(this, _profileInProgress, null);
28
- __privateAdd(this, _profileStartTs, null);
29
- __privateAdd(this, _trailAnimationFrameId, null);
30
- __privateAdd(this, _recordedTrailingSpans, []);
31
- this.lastFrameTime = 0;
32
- this.measureTrailingFrames = (measurementStartTs, lastFrameTime, profileStartTs) => {
33
- const currentFrameTime = performance.now();
34
- const frameLength = currentFrameTime - lastFrameTime;
35
- __privateGet(this, _recordedTrailingSpans).push(frameLength);
36
- if (currentFrameTime - measurementStartTs < POST_STORM_WINDOW) {
37
- __privateSet(this, _trailAnimationFrameId, requestAnimationFrame(
38
- () => this.measureTrailingFrames(measurementStartTs, currentFrameTime, profileStartTs)
39
- ));
40
- } else {
41
- const slowFrames = processRecordedSpans(__privateGet(this, _recordedTrailingSpans));
42
- const slowFramesTime = slowFrames.reduce((acc, val) => acc + val, 0);
43
- writeSceneLog(
44
- this.constructor.name,
45
- "Profile tail recorded, slow frames duration:",
46
- slowFramesTime,
47
- slowFrames,
48
- __privateGet(this, _profileInProgress)
49
- );
50
- __privateSet(this, _recordedTrailingSpans, []);
51
- const n = performance.now();
52
- const profileDuration = measurementStartTs - profileStartTs;
53
- writeSceneLog(
54
- this.constructor.name,
55
- "Stoped recording, total measured time (network included):",
56
- profileDuration + slowFramesTime
57
- );
58
- __privateSet(this, _trailAnimationFrameId, null);
59
- performance.measure("DashboardInteraction tail", {
60
- start: measurementStartTs,
61
- end: measurementStartTs + n
62
- });
63
- performance.measure("DashboardInteraction", {
64
- start: profileStartTs,
65
- end: profileStartTs + profileDuration + slowFramesTime
66
- });
67
- if (this.queryController.state.onProfileComplete) {
68
- this.queryController.state.onProfileComplete({
69
- origin: __privateGet(this, _profileInProgress).origin,
70
- crumbs: __privateGet(this, _profileInProgress).crumbs,
71
- duration: profileDuration + slowFramesTime
72
- });
73
- }
74
- if (window.__runs) {
75
- window.__runs += `${Date.now()}, ${profileDuration + slowFramesTime}
76
- `;
77
- } else {
78
- window.__runs = `${Date.now()}, ${profileDuration + slowFramesTime}
79
- `;
80
- }
81
- }
82
- };
83
- }
84
- startProfile(name) {
85
- if (__privateGet(this, _trailAnimationFrameId)) {
86
- cancelAnimationFrame(__privateGet(this, _trailAnimationFrameId));
87
- __privateSet(this, _trailAnimationFrameId, null);
88
- writeSceneLog(this.constructor.name, "New profile: Stopped recording frames");
89
- }
90
- __privateSet(this, _profileInProgress, { origin: name, crumbs: [] });
91
- __privateSet(this, _profileStartTs, performance.now());
92
- writeSceneLog(this.constructor.name, "Profile started:", __privateGet(this, _profileInProgress), __privateGet(this, _profileStartTs));
93
- }
94
- recordProfileTail(measurementStartTime, profileStartTs) {
95
- __privateSet(this, _trailAnimationFrameId, requestAnimationFrame(
96
- () => this.measureTrailingFrames(measurementStartTime, measurementStartTime, profileStartTs)
97
- ));
98
- }
99
- tryCompletingProfile() {
100
- writeSceneLog(this.constructor.name, "Trying to complete profile", __privateGet(this, _profileInProgress));
101
- if (this.queryController.runningQueriesCount() === 0 && __privateGet(this, _profileInProgress)) {
102
- writeSceneLog(this.constructor.name, "All queries completed, stopping profile");
103
- this.recordProfileTail(performance.now(), __privateGet(this, _profileStartTs));
104
- }
105
- }
106
- isTailRecording() {
107
- return Boolean(__privateGet(this, _trailAnimationFrameId));
108
- }
109
- cancelTailRecording() {
110
- if (__privateGet(this, _trailAnimationFrameId)) {
111
- cancelAnimationFrame(__privateGet(this, _trailAnimationFrameId));
112
- __privateSet(this, _trailAnimationFrameId, null);
113
- writeSceneLog(this.constructor.name, "Cancelled recording frames, new profile started");
114
- }
115
- }
116
- addCrumb(crumb) {
117
- if (__privateGet(this, _profileInProgress)) {
118
- __privateGet(this, _profileInProgress).crumbs.push(crumb);
119
- }
120
- }
121
- }
122
- _profileInProgress = new WeakMap();
123
- _profileStartTs = new WeakMap();
124
- _trailAnimationFrameId = new WeakMap();
125
- _recordedTrailingSpans = new WeakMap();
126
- function processRecordedSpans(spans) {
127
- for (let i = spans.length - 1; i >= 0; i--) {
128
- if (spans[i] > SPAN_THRESHOLD) {
129
- return spans.slice(0, i + 1);
130
- }
131
- }
132
- return [spans[0]];
133
- }
134
-
135
- export { SceneRenderProfiler };
136
- //# sourceMappingURL=SceneRenderProfiler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SceneRenderProfiler.js","sources":["../../../src/behaviors/SceneRenderProfiler.ts"],"sourcesContent":["import { writeSceneLog } from '../utils/writeSceneLog';\nimport { SceneQueryControllerLike } from './types';\n\nconst POST_STORM_WINDOW = 2000; // Time after last query to observe slow frames\nconst SPAN_THRESHOLD = 30; // Frames longer than this will be considered slow\n\nexport class SceneRenderProfiler {\n #profileInProgress: {\n // Profile origin, i.e. scene refresh picker\n origin: string;\n crumbs: string[];\n } | null = null;\n\n #profileStartTs: number | null = null;\n #trailAnimationFrameId: number | null = null;\n\n // Will keep measured lengths trailing frames\n #recordedTrailingSpans: number[] = [];\n\n lastFrameTime: number = 0;\n\n public constructor(private queryController: SceneQueryControllerLike) {}\n\n public startProfile(name: string) {\n if (this.#trailAnimationFrameId) {\n cancelAnimationFrame(this.#trailAnimationFrameId);\n this.#trailAnimationFrameId = null;\n\n writeSceneLog(this.constructor.name, 'New profile: Stopped recording frames');\n }\n\n this.#profileInProgress = { origin: name, crumbs: [] };\n this.#profileStartTs = performance.now();\n writeSceneLog(this.constructor.name, 'Profile started:', this.#profileInProgress, this.#profileStartTs);\n }\n\n private recordProfileTail(measurementStartTime: number, profileStartTs: number) {\n this.#trailAnimationFrameId = requestAnimationFrame(() =>\n this.measureTrailingFrames(measurementStartTime, measurementStartTime, profileStartTs)\n );\n }\n\n private measureTrailingFrames = (measurementStartTs: number, lastFrameTime: number, profileStartTs: number) => {\n const currentFrameTime = performance.now();\n const frameLength = currentFrameTime - lastFrameTime;\n this.#recordedTrailingSpans.push(frameLength);\n\n if (currentFrameTime - measurementStartTs! < POST_STORM_WINDOW) {\n this.#trailAnimationFrameId = requestAnimationFrame(() =>\n this.measureTrailingFrames(measurementStartTs, currentFrameTime, profileStartTs)\n );\n } else {\n const slowFrames = processRecordedSpans(this.#recordedTrailingSpans);\n const slowFramesTime = slowFrames.reduce((acc, val) => acc + val, 0);\n writeSceneLog(\n this.constructor.name,\n 'Profile tail recorded, slow frames duration:',\n slowFramesTime,\n slowFrames,\n this.#profileInProgress\n );\n\n this.#recordedTrailingSpans = [];\n\n // Using performance api to calculate sum of all network requests time starting at performance.now() -profileDuration - slowFramesTime\n // const entries = performance.getEntriesByType('resource');\n\n const n = performance.now();\n\n const profileDuration = measurementStartTs - profileStartTs;\n writeSceneLog(\n this.constructor.name,\n 'Stoped recording, total measured time (network included):',\n profileDuration + slowFramesTime\n );\n this.#trailAnimationFrameId = null;\n performance.measure('DashboardInteraction tail', {\n start: measurementStartTs,\n end: measurementStartTs + n,\n });\n performance.measure('DashboardInteraction', {\n start: profileStartTs,\n end: profileStartTs + profileDuration + slowFramesTime,\n });\n\n if (this.queryController.state.onProfileComplete) {\n this.queryController.state.onProfileComplete({\n origin: this.#profileInProgress!.origin,\n crumbs: this.#profileInProgress!.crumbs,\n duration: profileDuration + slowFramesTime,\n });\n }\n // @ts-ignore\n if (window.__runs) {\n // @ts-ignore\n window.__runs += `${Date.now()}, ${profileDuration + slowFramesTime}\\n`;\n } else {\n // @ts-ignore\n window.__runs = `${Date.now()}, ${profileDuration + slowFramesTime}\\n`;\n }\n }\n };\n\n public tryCompletingProfile() {\n writeSceneLog(this.constructor.name, 'Trying to complete profile', this.#profileInProgress);\n if (this.queryController.runningQueriesCount() === 0 && this.#profileInProgress) {\n writeSceneLog(this.constructor.name, 'All queries completed, stopping profile');\n this.recordProfileTail(performance.now(), this.#profileStartTs!);\n }\n }\n\n public isTailRecording() {\n return Boolean(this.#trailAnimationFrameId);\n }\n public cancelTailRecording() {\n if (this.#trailAnimationFrameId) {\n cancelAnimationFrame(this.#trailAnimationFrameId);\n this.#trailAnimationFrameId = null;\n writeSceneLog(this.constructor.name, 'Cancelled recording frames, new profile started');\n }\n }\n\n public addCrumb(crumb: string) {\n if (this.#profileInProgress) {\n this.#profileInProgress.crumbs.push(crumb);\n }\n }\n}\n\nfunction processRecordedSpans(spans: number[]) {\n // identifie last span in spans that's bigger than 50\n for (let i = spans.length - 1; i >= 0; i--) {\n if (spans[i] > SPAN_THRESHOLD) {\n return spans.slice(0, i + 1);\n }\n }\n return [spans[0]];\n}\n"],"names":[],"mappings":"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"}
@@ -1,19 +0,0 @@
1
- import { isQueryController } from '../../behaviors/SceneQueryController.js';
2
-
3
- function getQueryController(sceneObject) {
4
- let parent = sceneObject;
5
- while (parent) {
6
- if (parent.state.$behaviors) {
7
- for (const behavior of parent.state.$behaviors) {
8
- if (isQueryController(behavior)) {
9
- return behavior;
10
- }
11
- }
12
- }
13
- parent = parent.parent;
14
- }
15
- return void 0;
16
- }
17
-
18
- export { getQueryController };
19
- //# sourceMappingURL=getQueryController.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getQueryController.js","sources":["../../../../src/core/sceneGraph/getQueryController.ts"],"sourcesContent":["import { isQueryController } from '../../behaviors/SceneQueryController';\nimport { SceneQueryControllerLike } from '../../behaviors/types';\nimport { SceneObject } from '../types';\n\n/**\n * Returns the closest query controller undefined if none found\n */\nexport function getQueryController(sceneObject: SceneObject): SceneQueryControllerLike | undefined {\n let parent: SceneObject | undefined = sceneObject;\n\n while (parent) {\n if (parent.state.$behaviors) {\n for (const behavior of parent.state.$behaviors) {\n if (isQueryController(behavior)) {\n return behavior;\n }\n }\n }\n parent = parent.parent;\n }\n\n return undefined;\n}\n"],"names":[],"mappings":";;AAOO,SAAS,mBAAmB,WAAgE,EAAA;AACjG,EAAA,IAAI,MAAkC,GAAA,WAAA,CAAA;AAEtC,EAAA,OAAO,MAAQ,EAAA;AACb,IAAI,IAAA,MAAA,CAAO,MAAM,UAAY,EAAA;AAC3B,MAAW,KAAA,MAAA,QAAA,IAAY,MAAO,CAAA,KAAA,CAAM,UAAY,EAAA;AAC9C,QAAI,IAAA,iBAAA,CAAkB,QAAQ,CAAG,EAAA;AAC/B,UAAO,OAAA,QAAA,CAAA;AAAA,SACT;AAAA,OACF;AAAA,KACF;AACA,IAAA,MAAA,GAAS,MAAO,CAAA,MAAA,CAAA;AAAA,GAClB;AAEA,EAAO,OAAA,KAAA,CAAA,CAAA;AACT;;;;"}