@backstage/plugin-scaffolder 1.19.3 → 1.19.4-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/alpha/package.json +1 -1
  3. package/dist/alpha.esm.js +3 -8
  4. package/dist/alpha.esm.js.map +1 -1
  5. package/dist/{esm/routes-BvToNy4N.esm.js → api.esm.js} +2 -46
  6. package/dist/api.esm.js.map +1 -0
  7. package/dist/components/ActionsPage/ActionsPage.esm.js +186 -0
  8. package/dist/components/ActionsPage/ActionsPage.esm.js.map +1 -0
  9. package/dist/components/FileBrowser/FileBrowser.esm.js +90 -0
  10. package/dist/components/FileBrowser/FileBrowser.esm.js.map +1 -0
  11. package/dist/components/ListTasksPage/ListTasksPage.esm.js +120 -0
  12. package/dist/components/ListTasksPage/ListTasksPage.esm.js.map +1 -0
  13. package/dist/components/ListTasksPage/OwnerListPicker.esm.js +88 -0
  14. package/dist/components/ListTasksPage/OwnerListPicker.esm.js.map +1 -0
  15. package/dist/components/ListTasksPage/columns/CreatedAtColumn.esm.js +13 -0
  16. package/dist/components/ListTasksPage/columns/CreatedAtColumn.esm.js.map +1 -0
  17. package/dist/components/ListTasksPage/columns/OwnerEntityColumn.esm.js +31 -0
  18. package/dist/components/ListTasksPage/columns/OwnerEntityColumn.esm.js.map +1 -0
  19. package/dist/components/ListTasksPage/columns/TaskStatusColumn.esm.js +17 -0
  20. package/dist/components/ListTasksPage/columns/TaskStatusColumn.esm.js.map +1 -0
  21. package/dist/components/ListTasksPage/columns/TemplateTitleColumn.esm.js +21 -0
  22. package/dist/components/ListTasksPage/columns/TemplateTitleColumn.esm.js.map +1 -0
  23. package/dist/components/OngoingTask/ContextMenu.esm.js +84 -0
  24. package/dist/components/OngoingTask/ContextMenu.esm.js.map +1 -0
  25. package/dist/components/OngoingTask/OngoingTask.esm.js +169 -0
  26. package/dist/components/OngoingTask/OngoingTask.esm.js.map +1 -0
  27. package/dist/components/Router/Router.esm.js +105 -0
  28. package/dist/components/Router/Router.esm.js.map +1 -0
  29. package/dist/components/Router/index.esm.js +2 -0
  30. package/dist/components/Router/index.esm.js.map +1 -0
  31. package/dist/components/TemplateTypePicker/TemplateTypePicker.esm.js +70 -0
  32. package/dist/components/TemplateTypePicker/TemplateTypePicker.esm.js.map +1 -0
  33. package/dist/components/fields/EntityNamePicker/EntityNamePicker.esm.js +34 -0
  34. package/dist/components/fields/EntityNamePicker/EntityNamePicker.esm.js.map +1 -0
  35. package/dist/components/fields/EntityNamePicker/schema.esm.js +8 -0
  36. package/dist/components/fields/EntityNamePicker/schema.esm.js.map +1 -0
  37. package/dist/components/fields/EntityNamePicker/validation.esm.js +12 -0
  38. package/dist/components/fields/EntityNamePicker/validation.esm.js.map +1 -0
  39. package/dist/components/fields/EntityPicker/EntityPicker.esm.js +151 -0
  40. package/dist/components/fields/EntityPicker/EntityPicker.esm.js.map +1 -0
  41. package/dist/components/fields/EntityPicker/schema.esm.js +29 -0
  42. package/dist/components/fields/EntityPicker/schema.esm.js.map +1 -0
  43. package/dist/components/fields/EntityTagsPicker/EntityTagsPicker.esm.js +84 -0
  44. package/dist/components/fields/EntityTagsPicker/EntityTagsPicker.esm.js.map +1 -0
  45. package/dist/components/fields/EntityTagsPicker/schema.esm.js +15 -0
  46. package/dist/components/fields/EntityTagsPicker/schema.esm.js.map +1 -0
  47. package/dist/components/fields/MultiEntityPicker/MultiEntityPicker.esm.js +168 -0
  48. package/dist/components/fields/MultiEntityPicker/MultiEntityPicker.esm.js.map +1 -0
  49. package/dist/components/fields/MultiEntityPicker/schema.esm.js +23 -0
  50. package/dist/components/fields/MultiEntityPicker/schema.esm.js.map +1 -0
  51. package/dist/components/fields/MyGroupsPicker/MyGroupsPicker.esm.js +83 -0
  52. package/dist/components/fields/MyGroupsPicker/MyGroupsPicker.esm.js.map +1 -0
  53. package/dist/components/fields/MyGroupsPicker/schema.esm.js +14 -0
  54. package/dist/components/fields/MyGroupsPicker/schema.esm.js.map +1 -0
  55. package/dist/components/fields/OwnedEntityPicker/OwnedEntityPicker.esm.js +68 -0
  56. package/dist/components/fields/OwnedEntityPicker/OwnedEntityPicker.esm.js.map +1 -0
  57. package/dist/components/fields/OwnedEntityPicker/schema.esm.js +24 -0
  58. package/dist/components/fields/OwnedEntityPicker/schema.esm.js.map +1 -0
  59. package/dist/components/fields/OwnerPicker/OwnerPicker.esm.js +37 -0
  60. package/dist/components/fields/OwnerPicker/OwnerPicker.esm.js.map +1 -0
  61. package/dist/components/fields/OwnerPicker/schema.esm.js +24 -0
  62. package/dist/components/fields/OwnerPicker/schema.esm.js.map +1 -0
  63. package/dist/components/fields/RepoUrlPicker/AzureRepoPicker.esm.js +75 -0
  64. package/dist/components/fields/RepoUrlPicker/AzureRepoPicker.esm.js.map +1 -0
  65. package/dist/components/fields/RepoUrlPicker/BitbucketRepoPicker.esm.js +80 -0
  66. package/dist/components/fields/RepoUrlPicker/BitbucketRepoPicker.esm.js.map +1 -0
  67. package/dist/components/fields/RepoUrlPicker/GerritRepoPicker.esm.js +38 -0
  68. package/dist/components/fields/RepoUrlPicker/GerritRepoPicker.esm.js.map +1 -0
  69. package/dist/components/fields/RepoUrlPicker/GiteaRepoPicker.esm.js +44 -0
  70. package/dist/components/fields/RepoUrlPicker/GiteaRepoPicker.esm.js.map +1 -0
  71. package/dist/components/fields/RepoUrlPicker/GithubRepoPicker.esm.js +42 -0
  72. package/dist/components/fields/RepoUrlPicker/GithubRepoPicker.esm.js.map +1 -0
  73. package/dist/components/fields/RepoUrlPicker/GitlabRepoPicker.esm.js +44 -0
  74. package/dist/components/fields/RepoUrlPicker/GitlabRepoPicker.esm.js.map +1 -0
  75. package/dist/components/fields/RepoUrlPicker/RepoUrlPicker.esm.js +200 -0
  76. package/dist/components/fields/RepoUrlPicker/RepoUrlPicker.esm.js.map +1 -0
  77. package/dist/components/fields/RepoUrlPicker/RepoUrlPickerHost.esm.js +56 -0
  78. package/dist/components/fields/RepoUrlPicker/RepoUrlPickerHost.esm.js.map +1 -0
  79. package/dist/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.esm.js +48 -0
  80. package/dist/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.esm.js.map +1 -0
  81. package/dist/components/fields/RepoUrlPicker/schema.esm.js +32 -0
  82. package/dist/components/fields/RepoUrlPicker/schema.esm.js.map +1 -0
  83. package/dist/components/fields/RepoUrlPicker/utils.esm.js +46 -0
  84. package/dist/components/fields/RepoUrlPicker/utils.esm.js.map +1 -0
  85. package/dist/components/fields/RepoUrlPicker/validation.esm.js +43 -0
  86. package/dist/components/fields/RepoUrlPicker/validation.esm.js.map +1 -0
  87. package/dist/components/fields/SecretInput/SecretInput.esm.js +48 -0
  88. package/dist/components/fields/SecretInput/SecretInput.esm.js.map +1 -0
  89. package/dist/components/fields/utils.esm.js +15 -0
  90. package/dist/components/fields/utils.esm.js.map +1 -0
  91. package/dist/deprecated.esm.js +13 -0
  92. package/dist/deprecated.esm.js.map +1 -0
  93. package/dist/extensions/default.esm.js +72 -0
  94. package/dist/extensions/default.esm.js.map +1 -0
  95. package/dist/index.esm.js +13 -214
  96. package/dist/index.esm.js.map +1 -1
  97. package/dist/lib/download/helpers.esm.js +11 -0
  98. package/dist/lib/download/helpers.esm.js.map +1 -0
  99. package/dist/lib/filesystem/WebFileSystemAccess.esm.js +56 -0
  100. package/dist/lib/filesystem/WebFileSystemAccess.esm.js.map +1 -0
  101. package/dist/next/TemplateEditorPage/CustomFieldExplorer.esm.js +151 -0
  102. package/dist/next/TemplateEditorPage/CustomFieldExplorer.esm.js.map +1 -0
  103. package/dist/next/TemplateEditorPage/DirectoryEditorContext.esm.js +186 -0
  104. package/dist/next/TemplateEditorPage/DirectoryEditorContext.esm.js.map +1 -0
  105. package/dist/next/TemplateEditorPage/DryRunContext.esm.js +110 -0
  106. package/dist/next/TemplateEditorPage/DryRunContext.esm.js.map +1 -0
  107. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResults.esm.js +70 -0
  108. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResults.esm.js.map +1 -0
  109. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsList.esm.js +98 -0
  110. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsList.esm.js.map +1 -0
  111. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.esm.js +31 -0
  112. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.esm.js.map +1 -0
  113. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsView.esm.js +145 -0
  114. package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsView.esm.js.map +1 -0
  115. package/dist/next/TemplateEditorPage/DryRunResults/IconLink.esm.js +25 -0
  116. package/dist/next/TemplateEditorPage/DryRunResults/IconLink.esm.js.map +1 -0
  117. package/dist/next/TemplateEditorPage/DryRunResults/TaskPageLinks.esm.js +40 -0
  118. package/dist/next/TemplateEditorPage/DryRunResults/TaskPageLinks.esm.js.map +1 -0
  119. package/dist/next/TemplateEditorPage/DryRunResults/TaskStatusStepper.esm.js +135 -0
  120. package/dist/next/TemplateEditorPage/DryRunResults/TaskStatusStepper.esm.js.map +1 -0
  121. package/dist/next/TemplateEditorPage/TemplateEditor.esm.js +52 -0
  122. package/dist/next/TemplateEditorPage/TemplateEditor.esm.js.map +1 -0
  123. package/dist/next/TemplateEditorPage/TemplateEditorBrowser.esm.js +74 -0
  124. package/dist/next/TemplateEditorPage/TemplateEditorBrowser.esm.js.map +1 -0
  125. package/dist/next/TemplateEditorPage/TemplateEditorForm.esm.js +174 -0
  126. package/dist/next/TemplateEditorPage/TemplateEditorForm.esm.js.map +1 -0
  127. package/dist/next/TemplateEditorPage/TemplateEditorIntro.esm.js +93 -0
  128. package/dist/next/TemplateEditorPage/TemplateEditorIntro.esm.js.map +1 -0
  129. package/dist/next/TemplateEditorPage/TemplateEditorPage.esm.js +82 -0
  130. package/dist/next/TemplateEditorPage/TemplateEditorPage.esm.js.map +1 -0
  131. package/dist/next/TemplateEditorPage/TemplateEditorTextArea.esm.js +120 -0
  132. package/dist/next/TemplateEditorPage/TemplateEditorTextArea.esm.js.map +1 -0
  133. package/dist/next/TemplateEditorPage/TemplateFormPreviewer.esm.js +160 -0
  134. package/dist/next/TemplateEditorPage/TemplateFormPreviewer.esm.js.map +1 -0
  135. package/dist/next/TemplateListPage/RegisterExistingButton.esm.js +35 -0
  136. package/dist/next/TemplateListPage/RegisterExistingButton.esm.js.map +1 -0
  137. package/dist/next/TemplateListPage/TemplateListPage.esm.js +101 -0
  138. package/dist/next/TemplateListPage/TemplateListPage.esm.js.map +1 -0
  139. package/dist/next/TemplateWizardPage/TemplateWizardPage.esm.js +67 -0
  140. package/dist/next/TemplateWizardPage/TemplateWizardPage.esm.js.map +1 -0
  141. package/dist/next/TemplateWizardPage/TemplateWizardPageContextMenu.esm.js +60 -0
  142. package/dist/next/TemplateWizardPage/TemplateWizardPageContextMenu.esm.js.map +1 -0
  143. package/dist/plugin.esm.js +125 -0
  144. package/dist/plugin.esm.js.map +1 -0
  145. package/dist/routes.esm.js +47 -0
  146. package/dist/routes.esm.js.map +1 -0
  147. package/package.json +18 -18
  148. package/dist/esm/OngoingTask-ClfJCJcE.esm.js +0 -1575
  149. package/dist/esm/OngoingTask-ClfJCJcE.esm.js.map +0 -1
  150. package/dist/esm/index-UcPid-q1.esm.js +0 -2553
  151. package/dist/esm/index-UcPid-q1.esm.js.map +0 -1
  152. package/dist/esm/routes-BvToNy4N.esm.js.map +0 -1
@@ -0,0 +1,186 @@
1
+ import { ErrorPanel, Progress } from '@backstage/core-components';
2
+ import { useAsync, useRerender } from '@react-hookz/web';
3
+ import React, { createContext, useEffect, useContext } from 'react';
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __publicField = (obj, key, value) => {
8
+ __defNormalProp(obj, key + "" , value);
9
+ return value;
10
+ };
11
+ var __accessCheck = (obj, member, msg) => {
12
+ if (!member.has(obj))
13
+ throw TypeError("Cannot " + msg);
14
+ };
15
+ var __privateGet = (obj, member, getter) => {
16
+ __accessCheck(obj, member, "read from private field");
17
+ return member.get(obj);
18
+ };
19
+ var __privateAdd = (obj, member, value) => {
20
+ if (member.has(obj))
21
+ throw TypeError("Cannot add the same private member more than once");
22
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
23
+ };
24
+ var __privateSet = (obj, member, value, setter) => {
25
+ __accessCheck(obj, member, "write to private field");
26
+ member.set(obj, value);
27
+ return value;
28
+ };
29
+ var _access, _signalUpdate, _content, _savedContent, _access2, _listeners, _files, _selectedFile, _signalUpdate2;
30
+ const MAX_SIZE = 1024 * 1024;
31
+ const MAX_SIZE_MESSAGE = "This file is too large to be displayed";
32
+ class DirectoryEditorFileManager {
33
+ constructor(access, signalUpdate) {
34
+ __privateAdd(this, _access, void 0);
35
+ __privateAdd(this, _signalUpdate, void 0);
36
+ __privateAdd(this, _content, void 0);
37
+ __privateAdd(this, _savedContent, void 0);
38
+ __privateSet(this, _access, access);
39
+ __privateSet(this, _signalUpdate, signalUpdate);
40
+ }
41
+ get path() {
42
+ return __privateGet(this, _access).path;
43
+ }
44
+ get content() {
45
+ var _a;
46
+ return (_a = __privateGet(this, _content)) != null ? _a : MAX_SIZE_MESSAGE;
47
+ }
48
+ updateContent(content) {
49
+ if (__privateGet(this, _content) === void 0) {
50
+ return;
51
+ }
52
+ __privateSet(this, _content, content);
53
+ __privateGet(this, _signalUpdate).call(this);
54
+ }
55
+ get dirty() {
56
+ return __privateGet(this, _content) !== __privateGet(this, _savedContent);
57
+ }
58
+ async save() {
59
+ if (__privateGet(this, _content) !== void 0) {
60
+ await __privateGet(this, _access).save(__privateGet(this, _content));
61
+ __privateSet(this, _savedContent, __privateGet(this, _content));
62
+ __privateGet(this, _signalUpdate).call(this);
63
+ }
64
+ }
65
+ async reload() {
66
+ const file = await __privateGet(this, _access).file();
67
+ if (file.size > MAX_SIZE) {
68
+ if (__privateGet(this, _content) !== void 0) {
69
+ __privateSet(this, _content, void 0);
70
+ __privateSet(this, _savedContent, void 0);
71
+ __privateGet(this, _signalUpdate).call(this);
72
+ }
73
+ return;
74
+ }
75
+ const content = await file.text();
76
+ if (__privateGet(this, _content) !== content) {
77
+ __privateSet(this, _content, content);
78
+ __privateSet(this, _savedContent, content);
79
+ __privateGet(this, _signalUpdate).call(this);
80
+ }
81
+ }
82
+ }
83
+ _access = new WeakMap();
84
+ _signalUpdate = new WeakMap();
85
+ _content = new WeakMap();
86
+ _savedContent = new WeakMap();
87
+ class DirectoryEditorManager {
88
+ constructor(access) {
89
+ __privateAdd(this, _access2, void 0);
90
+ __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
91
+ __privateAdd(this, _files, []);
92
+ __privateAdd(this, _selectedFile, void 0);
93
+ __publicField(this, "setSelectedFile", (path) => {
94
+ const prev = __privateGet(this, _selectedFile);
95
+ const next = __privateGet(this, _files).find((file) => file.path === path);
96
+ if (prev !== next) {
97
+ __privateSet(this, _selectedFile, next);
98
+ __privateGet(this, _signalUpdate2).call(this);
99
+ }
100
+ });
101
+ __privateAdd(this, _signalUpdate2, () => {
102
+ __privateGet(this, _listeners).forEach((listener) => listener());
103
+ });
104
+ __privateSet(this, _access2, access);
105
+ }
106
+ get files() {
107
+ return __privateGet(this, _files);
108
+ }
109
+ get selectedFile() {
110
+ return __privateGet(this, _selectedFile);
111
+ }
112
+ get dirty() {
113
+ return __privateGet(this, _files).some((file) => file.dirty);
114
+ }
115
+ async save() {
116
+ await Promise.all(__privateGet(this, _files).map((file) => file.save()));
117
+ }
118
+ async reload() {
119
+ var _a;
120
+ const selectedPath = (_a = __privateGet(this, _selectedFile)) == null ? void 0 : _a.path;
121
+ const files = await __privateGet(this, _access2).listFiles();
122
+ const fileManagers = await Promise.all(
123
+ files.map(async (file) => {
124
+ const manager = new DirectoryEditorFileManager(
125
+ file,
126
+ __privateGet(this, _signalUpdate2)
127
+ );
128
+ await manager.reload();
129
+ return manager;
130
+ })
131
+ );
132
+ __privateGet(this, _files).length = 0;
133
+ __privateGet(this, _files).push(...fileManagers);
134
+ this.setSelectedFile(selectedPath);
135
+ __privateGet(this, _signalUpdate2).call(this);
136
+ }
137
+ subscribe(listener) {
138
+ __privateGet(this, _listeners).add(listener);
139
+ return () => {
140
+ __privateGet(this, _listeners).delete(listener);
141
+ };
142
+ }
143
+ }
144
+ _access2 = new WeakMap();
145
+ _listeners = new WeakMap();
146
+ _files = new WeakMap();
147
+ _selectedFile = new WeakMap();
148
+ _signalUpdate2 = new WeakMap();
149
+ const DirectoryEditorContext = createContext(
150
+ void 0
151
+ );
152
+ function useDirectoryEditor() {
153
+ const value = useContext(DirectoryEditorContext);
154
+ const rerender = useRerender();
155
+ useEffect(() => value == null ? void 0 : value.subscribe(rerender), [value, rerender]);
156
+ if (!value) {
157
+ throw new Error("must be used within a DirectoryEditorProvider");
158
+ }
159
+ return value;
160
+ }
161
+ function DirectoryEditorProvider(props) {
162
+ const { directory } = props;
163
+ const [{ result, error }, { execute }] = useAsync(
164
+ async (dir) => {
165
+ const manager = new DirectoryEditorManager(dir);
166
+ await manager.reload();
167
+ const firstYaml = manager.files.find((file) => file.path.match(/\.ya?ml$/));
168
+ if (firstYaml) {
169
+ manager.setSelectedFile(firstYaml.path);
170
+ }
171
+ return manager;
172
+ }
173
+ );
174
+ useEffect(() => {
175
+ execute(directory);
176
+ }, [execute, directory]);
177
+ if (error) {
178
+ return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
179
+ } else if (!result) {
180
+ return /* @__PURE__ */ React.createElement(Progress, null);
181
+ }
182
+ return /* @__PURE__ */ React.createElement(DirectoryEditorContext.Provider, { value: result }, props.children);
183
+ }
184
+
185
+ export { DirectoryEditorProvider, useDirectoryEditor };
186
+ //# sourceMappingURL=DirectoryEditorContext.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DirectoryEditorContext.esm.js","sources":["../../../src/next/TemplateEditorPage/DirectoryEditorContext.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ErrorPanel, Progress } from '@backstage/core-components';\nimport { useAsync, useRerender } from '@react-hookz/web';\nimport React, { createContext, ReactNode, useContext, useEffect } from 'react';\nimport {\n TemplateDirectoryAccess,\n TemplateFileAccess,\n} from '../../lib/filesystem';\n\nconst MAX_SIZE = 1024 * 1024;\nconst MAX_SIZE_MESSAGE = 'This file is too large to be displayed';\n\ninterface DirectoryEditorFile {\n /** The path of the file relative to the root directory */\n path: string;\n /** The staged content of the file */\n content: string;\n /** Whether the staged content matches what is on disk */\n dirty: boolean;\n\n /** Update the staged content of the file without saving */\n updateContent(content: string): void;\n /** Save the staged content of the file to disk */\n save(): Promise<void>;\n /** Reload the staged content of the file from disk */\n reload(): Promise<void>;\n}\n\ninterface DirectoryEditor {\n /** A list of all files in the edited directory */\n files: Array<DirectoryEditorFile>;\n\n /** The currently selected file */\n selectedFile: DirectoryEditorFile | undefined;\n /** Switch the selected file */\n setSelectedFile(path: string | undefined): void;\n\n /** Save all files to disk */\n save(): Promise<void>;\n /** Reload all files from disk */\n reload(): Promise<void>;\n\n subscribe(listener: () => void): () => void;\n}\n\nclass DirectoryEditorFileManager implements DirectoryEditorFile {\n readonly #access: TemplateFileAccess;\n readonly #signalUpdate: () => void;\n\n #content?: string;\n #savedContent?: string;\n\n constructor(access: TemplateFileAccess, signalUpdate: () => void) {\n this.#access = access;\n this.#signalUpdate = signalUpdate;\n }\n\n get path() {\n return this.#access.path;\n }\n\n get content() {\n return this.#content ?? MAX_SIZE_MESSAGE;\n }\n\n updateContent(content: string): void {\n if (this.#content === undefined) {\n return;\n }\n this.#content = content;\n this.#signalUpdate();\n }\n\n get dirty() {\n return this.#content !== this.#savedContent;\n }\n\n async save(): Promise<void> {\n if (this.#content !== undefined) {\n await this.#access.save(this.#content);\n this.#savedContent = this.#content;\n this.#signalUpdate();\n }\n }\n\n async reload(): Promise<void> {\n const file = await this.#access.file();\n if (file.size > MAX_SIZE) {\n if (this.#content !== undefined) {\n this.#content = undefined;\n this.#savedContent = undefined;\n this.#signalUpdate();\n }\n return;\n }\n\n const content = await file.text();\n if (this.#content !== content) {\n this.#content = content;\n this.#savedContent = content;\n this.#signalUpdate();\n }\n }\n}\n\nclass DirectoryEditorManager implements DirectoryEditor {\n readonly #access: TemplateDirectoryAccess;\n readonly #listeners = new Set<() => void>();\n\n #files: DirectoryEditorFile[] = [];\n #selectedFile: DirectoryEditorFile | undefined;\n\n constructor(access: TemplateDirectoryAccess) {\n this.#access = access;\n }\n\n get files() {\n return this.#files;\n }\n\n get selectedFile() {\n return this.#selectedFile;\n }\n\n setSelectedFile = (path: string | undefined): void => {\n const prev = this.#selectedFile;\n const next = this.#files.find(file => file.path === path);\n if (prev !== next) {\n this.#selectedFile = next;\n this.#signalUpdate();\n }\n };\n\n get dirty() {\n return this.#files.some(file => file.dirty);\n }\n\n async save(): Promise<void> {\n await Promise.all(this.#files.map(file => file.save()));\n }\n\n async reload(): Promise<void> {\n const selectedPath = this.#selectedFile?.path;\n\n const files = await this.#access.listFiles();\n const fileManagers = await Promise.all(\n files.map(async file => {\n const manager = new DirectoryEditorFileManager(\n file,\n this.#signalUpdate,\n );\n await manager.reload();\n return manager;\n }),\n );\n this.#files.length = 0;\n this.#files.push(...fileManagers);\n\n this.setSelectedFile(selectedPath);\n this.#signalUpdate();\n }\n\n subscribe(listener: () => void): () => void {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n #signalUpdate = () => {\n this.#listeners.forEach(listener => listener());\n };\n}\n\nconst DirectoryEditorContext = createContext<DirectoryEditor | undefined>(\n undefined,\n);\n\nexport function useDirectoryEditor(): DirectoryEditor {\n const value = useContext(DirectoryEditorContext);\n const rerender = useRerender();\n\n useEffect(() => value?.subscribe(rerender), [value, rerender]);\n\n if (!value) {\n throw new Error('must be used within a DirectoryEditorProvider');\n }\n return value;\n}\n\ninterface DirectoryEditorProviderProps {\n directory: TemplateDirectoryAccess;\n children?: ReactNode;\n}\n\nexport function DirectoryEditorProvider(props: DirectoryEditorProviderProps) {\n const { directory } = props;\n\n const [{ result, error }, { execute }] = useAsync(\n async (dir: TemplateDirectoryAccess) => {\n const manager = new DirectoryEditorManager(dir);\n await manager.reload();\n\n const firstYaml = manager.files.find(file => file.path.match(/\\.ya?ml$/));\n if (firstYaml) {\n manager.setSelectedFile(firstYaml.path);\n }\n\n return manager;\n },\n );\n\n useEffect(() => {\n execute(directory);\n }, [execute, directory]);\n\n if (error) {\n return <ErrorPanel error={error} />;\n } else if (!result) {\n return <Progress />;\n }\n\n return (\n <DirectoryEditorContext.Provider value={result}>\n {props.children}\n </DirectoryEditorContext.Provider>\n );\n}\n"],"names":["_access","_signalUpdate"],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import yaml from 'yaml';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import React, { createContext, useState, useRef, useCallback, useMemo, useContext } from 'react';
4
+ import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
5
+
6
+ const MAX_CONTENT_SIZE = 64 * 1024;
7
+ const CHUNK_SIZE = 32 * 1024;
8
+ const DryRunContext = createContext(void 0);
9
+ function base64EncodeContent(content) {
10
+ if (content.length > MAX_CONTENT_SIZE) {
11
+ return window.btoa("<file too large>");
12
+ }
13
+ try {
14
+ return window.btoa(content);
15
+ } catch {
16
+ const decoder = new TextEncoder();
17
+ const buffer = decoder.encode(content);
18
+ const chunks = new Array();
19
+ for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
20
+ chunks.push(
21
+ String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE))
22
+ );
23
+ }
24
+ return window.btoa(chunks.join(""));
25
+ }
26
+ }
27
+ function DryRunProvider(props) {
28
+ const scaffolderApi = useApi(scaffolderApiRef);
29
+ const [state, setState] = useState({
30
+ results: [],
31
+ selectedResult: void 0
32
+ });
33
+ const idRef = useRef(1);
34
+ const selectResult = useCallback((id) => {
35
+ setState((prevState) => {
36
+ const result = prevState.results.find((r) => r.id === id);
37
+ if (result === prevState.selectedResult) {
38
+ return prevState;
39
+ }
40
+ return {
41
+ results: prevState.results,
42
+ selectedResult: result
43
+ };
44
+ });
45
+ }, []);
46
+ const deleteResult = useCallback((id) => {
47
+ setState((prevState) => {
48
+ var _a;
49
+ const index = prevState.results.findIndex((r) => r.id === id);
50
+ if (index === -1) {
51
+ return prevState;
52
+ }
53
+ const newResults = prevState.results.slice();
54
+ const [deleted] = newResults.splice(index, 1);
55
+ return {
56
+ results: newResults,
57
+ selectedResult: ((_a = prevState.selectedResult) == null ? void 0 : _a.id) === deleted.id ? newResults[0] : prevState.selectedResult
58
+ };
59
+ });
60
+ }, []);
61
+ const execute = useCallback(
62
+ async (options) => {
63
+ if (!scaffolderApi.dryRun) {
64
+ throw new Error("Scaffolder API does not support dry-run");
65
+ }
66
+ const parsed = yaml.parse(options.templateContent);
67
+ const response = await scaffolderApi.dryRun({
68
+ template: parsed,
69
+ values: options.values,
70
+ secrets: {},
71
+ directoryContents: options.files.map((file) => ({
72
+ path: file.path,
73
+ base64Content: base64EncodeContent(file.content)
74
+ }))
75
+ });
76
+ const result = {
77
+ ...response,
78
+ id: idRef.current++
79
+ };
80
+ setState((prevState) => {
81
+ var _a;
82
+ return {
83
+ results: [...prevState.results, result],
84
+ selectedResult: (_a = prevState.selectedResult) != null ? _a : result
85
+ };
86
+ });
87
+ },
88
+ [scaffolderApi]
89
+ );
90
+ const dryRun = useMemo(
91
+ () => ({
92
+ ...state,
93
+ selectResult,
94
+ deleteResult,
95
+ execute
96
+ }),
97
+ [state, selectResult, deleteResult, execute]
98
+ );
99
+ return /* @__PURE__ */ React.createElement(DryRunContext.Provider, { value: dryRun }, props.children);
100
+ }
101
+ function useDryRun() {
102
+ const value = useContext(DryRunContext);
103
+ if (!value) {
104
+ throw new Error("must be used within a DryRunProvider");
105
+ }
106
+ return value;
107
+ }
108
+
109
+ export { DryRunProvider, base64EncodeContent, useDryRun };
110
+ //# sourceMappingURL=DryRunContext.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DryRunContext.esm.js","sources":["../../../src/next/TemplateEditorPage/DryRunContext.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport yaml from 'yaml';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport React, {\n createContext,\n ReactNode,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n scaffolderApiRef,\n ScaffolderDryRunResponse,\n} from '@backstage/plugin-scaffolder-react';\n\nconst MAX_CONTENT_SIZE = 64 * 1024;\nconst CHUNK_SIZE = 32 * 1024;\n\ninterface DryRunOptions {\n templateContent: string;\n values: JsonObject;\n files: Array<{ path: string; content: string }>;\n}\n\nexport interface DryRunResult extends ScaffolderDryRunResponse {\n id: number;\n}\n\ninterface DryRun {\n results: DryRunResult[];\n selectedResult: DryRunResult | undefined;\n\n selectResult(id: number): void;\n deleteResult(id: number): void;\n execute(options: DryRunOptions): Promise<void>;\n}\n\nconst DryRunContext = createContext<DryRun | undefined>(undefined);\n\ninterface DryRunProviderProps {\n children: ReactNode;\n}\n\nexport function base64EncodeContent(content: string): string {\n if (content.length > MAX_CONTENT_SIZE) {\n return window.btoa('<file too large>');\n }\n\n try {\n return window.btoa(content);\n } catch {\n const decoder = new TextEncoder();\n const buffer = decoder.encode(content);\n\n const chunks = new Array<string>();\n for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {\n chunks.push(\n String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE)),\n );\n }\n return window.btoa(chunks.join(''));\n }\n}\n\nexport function DryRunProvider(props: DryRunProviderProps) {\n const scaffolderApi = useApi(scaffolderApiRef);\n\n const [state, setState] = useState<\n Pick<DryRun, 'results' | 'selectedResult'>\n >({\n results: [],\n selectedResult: undefined,\n });\n const idRef = useRef(1);\n\n const selectResult = useCallback((id: number) => {\n setState(prevState => {\n const result = prevState.results.find(r => r.id === id);\n if (result === prevState.selectedResult) {\n return prevState;\n }\n return {\n results: prevState.results,\n selectedResult: result,\n };\n });\n }, []);\n\n const deleteResult = useCallback((id: number) => {\n setState(prevState => {\n const index = prevState.results.findIndex(r => r.id === id);\n if (index === -1) {\n return prevState;\n }\n const newResults = prevState.results.slice();\n const [deleted] = newResults.splice(index, 1);\n return {\n results: newResults,\n selectedResult:\n prevState.selectedResult?.id === deleted.id\n ? newResults[0]\n : prevState.selectedResult,\n };\n });\n }, []);\n\n const execute = useCallback(\n async (options: DryRunOptions) => {\n if (!scaffolderApi.dryRun) {\n throw new Error('Scaffolder API does not support dry-run');\n }\n\n const parsed = yaml.parse(options.templateContent);\n\n const response = await scaffolderApi.dryRun({\n template: parsed,\n values: options.values,\n secrets: {},\n directoryContents: options.files.map(file => ({\n path: file.path,\n base64Content: base64EncodeContent(file.content),\n })),\n });\n\n const result = {\n ...response,\n id: idRef.current++,\n };\n\n setState(prevState => ({\n results: [...prevState.results, result],\n selectedResult: prevState.selectedResult ?? result,\n }));\n },\n [scaffolderApi],\n );\n\n const dryRun = useMemo(\n () => ({\n ...state,\n selectResult,\n deleteResult,\n execute,\n }),\n [state, selectResult, deleteResult, execute],\n );\n\n return (\n <DryRunContext.Provider value={dryRun}>\n {props.children}\n </DryRunContext.Provider>\n );\n}\n\nexport function useDryRun(): DryRun {\n const value = useContext(DryRunContext);\n if (!value) {\n throw new Error('must be used within a DryRunProvider');\n }\n return value;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import Accordion from '@material-ui/core/Accordion';
2
+ import AccordionDetails from '@material-ui/core/AccordionDetails';
3
+ import AccordionSummary from '@material-ui/core/AccordionSummary';
4
+ import Divider from '@material-ui/core/Divider';
5
+ import { makeStyles } from '@material-ui/core/styles';
6
+ import Typography from '@material-ui/core/Typography';
7
+ import ExpandLessIcon from '@material-ui/icons/ExpandLess';
8
+ import { usePrevious } from '@react-hookz/web';
9
+ import React, { useState, useEffect } from 'react';
10
+ import { useDryRun } from '../DryRunContext.esm.js';
11
+ import { DryRunResultsList } from './DryRunResultsList.esm.js';
12
+ import { DryRunResultsView } from './DryRunResultsView.esm.js';
13
+
14
+ const useStyles = makeStyles((theme) => ({
15
+ header: {
16
+ height: 48,
17
+ minHeight: 0,
18
+ "&.Mui-expanded": {
19
+ height: 48,
20
+ minHeight: 0
21
+ }
22
+ },
23
+ content: {
24
+ display: "grid",
25
+ background: theme.palette.background.default,
26
+ gridTemplateColumns: "180px auto 1fr",
27
+ gridTemplateRows: "1fr",
28
+ padding: 0,
29
+ height: 400
30
+ }
31
+ }));
32
+ function DryRunResults() {
33
+ const classes = useStyles();
34
+ const dryRun = useDryRun();
35
+ const [expanded, setExpanded] = useState(false);
36
+ const [hidden, setHidden] = useState(true);
37
+ const resultsLength = dryRun.results.length;
38
+ const prevResultsLength = usePrevious(resultsLength);
39
+ useEffect(() => {
40
+ if (prevResultsLength === 0 && resultsLength === 1) {
41
+ setHidden(false);
42
+ setExpanded(true);
43
+ } else if (prevResultsLength === 1 && resultsLength === 0) {
44
+ setExpanded(false);
45
+ }
46
+ }, [prevResultsLength, resultsLength]);
47
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
48
+ Accordion,
49
+ {
50
+ variant: "outlined",
51
+ expanded,
52
+ hidden: resultsLength === 0 && hidden,
53
+ onChange: (_, exp) => setExpanded(exp),
54
+ onTransitionEnd: () => resultsLength === 0 && setHidden(true)
55
+ },
56
+ /* @__PURE__ */ React.createElement(
57
+ AccordionSummary,
58
+ {
59
+ className: classes.header,
60
+ expandIcon: /* @__PURE__ */ React.createElement(ExpandLessIcon, null)
61
+ },
62
+ /* @__PURE__ */ React.createElement(Typography, null, "Dry-run results")
63
+ ),
64
+ /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }),
65
+ /* @__PURE__ */ React.createElement(AccordionDetails, { className: classes.content }, /* @__PURE__ */ React.createElement(DryRunResultsList, null), /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }), /* @__PURE__ */ React.createElement(DryRunResultsView, null))
66
+ ));
67
+ }
68
+
69
+ export { DryRunResults };
70
+ //# sourceMappingURL=DryRunResults.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DryRunResults.esm.js","sources":["../../../../src/next/TemplateEditorPage/DryRunResults/DryRunResults.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Accordion from '@material-ui/core/Accordion';\nimport AccordionDetails from '@material-ui/core/AccordionDetails';\nimport AccordionSummary from '@material-ui/core/AccordionSummary';\nimport Divider from '@material-ui/core/Divider';\nimport { makeStyles } from '@material-ui/core/styles';\nimport Typography from '@material-ui/core/Typography';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandLess';\nimport { usePrevious } from '@react-hookz/web';\nimport React, { useEffect, useState } from 'react';\nimport { useDryRun } from '../DryRunContext';\nimport { DryRunResultsList } from './DryRunResultsList';\nimport { DryRunResultsView } from './DryRunResultsView';\n\nconst useStyles = makeStyles(theme => ({\n header: {\n height: 48,\n minHeight: 0,\n '&.Mui-expanded': {\n height: 48,\n minHeight: 0,\n },\n },\n content: {\n display: 'grid',\n background: theme.palette.background.default,\n gridTemplateColumns: '180px auto 1fr',\n gridTemplateRows: '1fr',\n padding: 0,\n height: 400,\n },\n}));\n\nexport function DryRunResults() {\n const classes = useStyles();\n const dryRun = useDryRun();\n const [expanded, setExpanded] = useState(false);\n const [hidden, setHidden] = useState(true);\n\n const resultsLength = dryRun.results.length;\n const prevResultsLength = usePrevious(resultsLength);\n useEffect(() => {\n if (prevResultsLength === 0 && resultsLength === 1) {\n setHidden(false);\n setExpanded(true);\n } else if (prevResultsLength === 1 && resultsLength === 0) {\n setExpanded(false);\n }\n }, [prevResultsLength, resultsLength]);\n\n return (\n <>\n <Accordion\n variant=\"outlined\"\n expanded={expanded}\n hidden={resultsLength === 0 && hidden}\n onChange={(_, exp) => setExpanded(exp)}\n onTransitionEnd={() => resultsLength === 0 && setHidden(true)}\n >\n <AccordionSummary\n className={classes.header}\n expandIcon={<ExpandMoreIcon />}\n >\n <Typography>Dry-run results</Typography>\n </AccordionSummary>\n <Divider orientation=\"horizontal\" />\n <AccordionDetails className={classes.content}>\n <DryRunResultsList />\n <Divider orientation=\"horizontal\" />\n <DryRunResultsView />\n </AccordionDetails>\n </Accordion>\n </>\n );\n}\n"],"names":["ExpandMoreIcon"],"mappings":";;;;;;;;;;;;;AA6BA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,MAAQ,EAAA;AAAA,IACN,MAAQ,EAAA,EAAA;AAAA,IACR,SAAW,EAAA,CAAA;AAAA,IACX,gBAAkB,EAAA;AAAA,MAChB,MAAQ,EAAA,EAAA;AAAA,MACR,SAAW,EAAA,CAAA;AAAA,KACb;AAAA,GACF;AAAA,EACA,OAAS,EAAA;AAAA,IACP,OAAS,EAAA,MAAA;AAAA,IACT,UAAA,EAAY,KAAM,CAAA,OAAA,CAAQ,UAAW,CAAA,OAAA;AAAA,IACrC,mBAAqB,EAAA,gBAAA;AAAA,IACrB,gBAAkB,EAAA,KAAA;AAAA,IAClB,OAAS,EAAA,CAAA;AAAA,IACT,MAAQ,EAAA,GAAA;AAAA,GACV;AACF,CAAE,CAAA,CAAA,CAAA;AAEK,SAAS,aAAgB,GAAA;AAC9B,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAA,MAAM,SAAS,SAAU,EAAA,CAAA;AACzB,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA,CAAA;AAC9C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,IAAI,CAAA,CAAA;AAEzC,EAAM,MAAA,aAAA,GAAgB,OAAO,OAAQ,CAAA,MAAA,CAAA;AACrC,EAAM,MAAA,iBAAA,GAAoB,YAAY,aAAa,CAAA,CAAA;AACnD,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,iBAAA,KAAsB,CAAK,IAAA,aAAA,KAAkB,CAAG,EAAA;AAClD,MAAA,SAAA,CAAU,KAAK,CAAA,CAAA;AACf,MAAA,WAAA,CAAY,IAAI,CAAA,CAAA;AAAA,KACP,MAAA,IAAA,iBAAA,KAAsB,CAAK,IAAA,aAAA,KAAkB,CAAG,EAAA;AACzD,MAAA,WAAA,CAAY,KAAK,CAAA,CAAA;AAAA,KACnB;AAAA,GACC,EAAA,CAAC,iBAAmB,EAAA,aAAa,CAAC,CAAA,CAAA;AAErC,EAAA,uBAEI,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,OAAQ,EAAA,UAAA;AAAA,MACR,QAAA;AAAA,MACA,MAAA,EAAQ,kBAAkB,CAAK,IAAA,MAAA;AAAA,MAC/B,QAAU,EAAA,CAAC,CAAG,EAAA,GAAA,KAAQ,YAAY,GAAG,CAAA;AAAA,MACrC,eAAiB,EAAA,MAAM,aAAkB,KAAA,CAAA,IAAK,UAAU,IAAI,CAAA;AAAA,KAAA;AAAA,oBAE5D,KAAA,CAAA,aAAA;AAAA,MAAC,gBAAA;AAAA,MAAA;AAAA,QACC,WAAW,OAAQ,CAAA,MAAA;AAAA,QACnB,UAAA,sCAAaA,cAAe,EAAA,IAAA,CAAA;AAAA,OAAA;AAAA,sBAE5B,KAAA,CAAA,aAAA,CAAC,kBAAW,iBAAe,CAAA;AAAA,KAC7B;AAAA,oBACA,KAAA,CAAA,aAAA,CAAC,OAAQ,EAAA,EAAA,WAAA,EAAY,YAAa,EAAA,CAAA;AAAA,oBACjC,KAAA,CAAA,aAAA,CAAA,gBAAA,EAAA,EAAiB,SAAW,EAAA,OAAA,CAAQ,2BAClC,KAAA,CAAA,aAAA,CAAA,iBAAA,EAAA,IAAkB,CACnB,kBAAA,KAAA,CAAA,aAAA,CAAC,WAAQ,WAAY,EAAA,YAAA,EAAa,CAClC,kBAAA,KAAA,CAAA,aAAA,CAAC,uBAAkB,CACrB,CAAA;AAAA,GAEJ,CAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,98 @@
1
+ import IconButton from '@material-ui/core/IconButton';
2
+ import List from '@material-ui/core/List';
3
+ import ListItem from '@material-ui/core/ListItem';
4
+ import ListItemIcon from '@material-ui/core/ListItemIcon';
5
+ import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
6
+ import ListItemText from '@material-ui/core/ListItemText';
7
+ import { makeStyles } from '@material-ui/core/styles';
8
+ import Cancel from '@material-ui/icons/Cancel';
9
+ import CheckIcon from '@material-ui/icons/Check';
10
+ import DeleteIcon from '@material-ui/icons/Delete';
11
+ import DownloadIcon from '@material-ui/icons/GetApp';
12
+ import React from 'react';
13
+ import { useDryRun } from '../DryRunContext.esm.js';
14
+ import { downloadBlob } from '../../../lib/download/helpers.esm.js';
15
+
16
+ const useStyles = makeStyles((theme) => ({
17
+ root: {
18
+ overflowY: "auto",
19
+ background: theme.palette.background.default
20
+ },
21
+ iconSuccess: {
22
+ minWidth: 0,
23
+ marginRight: theme.spacing(1),
24
+ color: theme.palette.status.ok
25
+ },
26
+ iconFailure: {
27
+ minWidth: 0,
28
+ marginRight: theme.spacing(1),
29
+ color: theme.palette.status.error
30
+ }
31
+ }));
32
+ function DryRunResultsList() {
33
+ const classes = useStyles();
34
+ const dryRun = useDryRun();
35
+ return /* @__PURE__ */ React.createElement(List, { className: classes.root, dense: true }, dryRun.results.map((result) => {
36
+ var _a;
37
+ const failed = result.log.some((l) => l.body.status === "failed");
38
+ let isLoading = false;
39
+ async function downloadResult() {
40
+ isLoading = true;
41
+ await downloadDirectoryContents(
42
+ result.directoryContents,
43
+ `dry-run-result-${result.id}.zip`
44
+ );
45
+ isLoading = false;
46
+ }
47
+ return /* @__PURE__ */ React.createElement(
48
+ ListItem,
49
+ {
50
+ button: true,
51
+ key: result.id,
52
+ selected: ((_a = dryRun.selectedResult) == null ? void 0 : _a.id) === result.id,
53
+ onClick: () => dryRun.selectResult(result.id)
54
+ },
55
+ /* @__PURE__ */ React.createElement(
56
+ ListItemIcon,
57
+ {
58
+ className: failed ? classes.iconFailure : classes.iconSuccess
59
+ },
60
+ failed ? /* @__PURE__ */ React.createElement(Cancel, null) : /* @__PURE__ */ React.createElement(CheckIcon, null)
61
+ ),
62
+ /* @__PURE__ */ React.createElement(ListItemText, { primary: `Result ${result.id}` }),
63
+ /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
64
+ IconButton,
65
+ {
66
+ edge: "end",
67
+ "aria-label": "download",
68
+ title: "Download as .zip",
69
+ disabled: isLoading,
70
+ onClick: () => downloadResult()
71
+ },
72
+ /* @__PURE__ */ React.createElement(DownloadIcon, null)
73
+ ), /* @__PURE__ */ React.createElement(
74
+ IconButton,
75
+ {
76
+ edge: "end",
77
+ "aria-label": "delete",
78
+ title: "Delete result",
79
+ onClick: () => dryRun.deleteResult(result.id)
80
+ },
81
+ /* @__PURE__ */ React.createElement(DeleteIcon, null)
82
+ ))
83
+ );
84
+ }));
85
+ }
86
+ async function downloadDirectoryContents(directoryContents, name) {
87
+ const { default: JSZip } = await import('jszip');
88
+ const zip = new JSZip();
89
+ for (const d of directoryContents) {
90
+ const converted = atob(d.base64Content);
91
+ await zip.file(d.path, converted);
92
+ }
93
+ const blob = await zip.generateAsync({ type: "blob" });
94
+ downloadBlob(blob, name);
95
+ }
96
+
97
+ export { DryRunResultsList };
98
+ //# sourceMappingURL=DryRunResultsList.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DryRunResultsList.esm.js","sources":["../../../../src/next/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport IconButton from '@material-ui/core/IconButton';\nimport List from '@material-ui/core/List';\nimport ListItem from '@material-ui/core/ListItem';\nimport ListItemIcon from '@material-ui/core/ListItemIcon';\nimport ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport { makeStyles } from '@material-ui/core/styles';\nimport CancelIcon from '@material-ui/icons/Cancel';\nimport CheckIcon from '@material-ui/icons/Check';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport DownloadIcon from '@material-ui/icons/GetApp';\nimport React from 'react';\nimport { useDryRun } from '../DryRunContext';\nimport { downloadBlob } from '../../../lib/download';\n\nconst useStyles = makeStyles(theme => ({\n root: {\n overflowY: 'auto',\n background: theme.palette.background.default,\n },\n iconSuccess: {\n minWidth: 0,\n marginRight: theme.spacing(1),\n color: theme.palette.status.ok,\n },\n iconFailure: {\n minWidth: 0,\n marginRight: theme.spacing(1),\n color: theme.palette.status.error,\n },\n}));\n\nexport function DryRunResultsList() {\n const classes = useStyles();\n const dryRun = useDryRun();\n\n return (\n <List className={classes.root} dense>\n {dryRun.results.map(result => {\n const failed = result.log.some(l => l.body.status === 'failed');\n let isLoading = false;\n\n async function downloadResult() {\n isLoading = true;\n await downloadDirectoryContents(\n result.directoryContents,\n `dry-run-result-${result.id}.zip`,\n );\n isLoading = false;\n }\n\n return (\n <ListItem\n button\n key={result.id}\n selected={dryRun.selectedResult?.id === result.id}\n onClick={() => dryRun.selectResult(result.id)}\n >\n <ListItemIcon\n className={failed ? classes.iconFailure : classes.iconSuccess}\n >\n {failed ? <CancelIcon /> : <CheckIcon />}\n </ListItemIcon>\n <ListItemText primary={`Result ${result.id}`} />\n <ListItemSecondaryAction>\n <IconButton\n edge=\"end\"\n aria-label=\"download\"\n title=\"Download as .zip\"\n disabled={isLoading}\n onClick={() => downloadResult()}\n >\n <DownloadIcon />\n </IconButton>\n <IconButton\n edge=\"end\"\n aria-label=\"delete\"\n title=\"Delete result\"\n onClick={() => dryRun.deleteResult(result.id)}\n >\n <DeleteIcon />\n </IconButton>\n </ListItemSecondaryAction>\n </ListItem>\n );\n })}\n </List>\n );\n}\n\nasync function downloadDirectoryContents(\n directoryContents: {\n path: string;\n base64Content: string;\n executable: boolean;\n }[],\n name: string,\n) {\n const { default: JSZip } = await import('jszip');\n const zip = new JSZip();\n\n for (const d of directoryContents) {\n // Decode text content from base64 to ascii\n const converted = atob(d.base64Content);\n\n // add folder/file to zip\n await zip.file(d.path, converted);\n }\n\n const blob = await zip.generateAsync({ type: 'blob' });\n downloadBlob(blob, name);\n}\n"],"names":["CancelIcon"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { makeStyles } from '@material-ui/core/styles';
2
+ import Divider from '@material-ui/core/Divider';
3
+ import React, { Children } from 'react';
4
+ import classNames from 'classnames';
5
+
6
+ const useStyles = makeStyles((theme) => ({
7
+ root: {
8
+ display: "grid",
9
+ gridTemplateColumns: "280px auto 3fr",
10
+ gridTemplateRows: "1fr"
11
+ },
12
+ child: {
13
+ overflowY: "auto",
14
+ height: "100%",
15
+ minHeight: 0
16
+ },
17
+ firstChild: {
18
+ background: theme.palette.background.paper
19
+ }
20
+ }));
21
+ function DryRunResultsSplitView(props) {
22
+ const classes = useStyles();
23
+ const childArray = Children.toArray(props.children);
24
+ if (childArray.length !== 2) {
25
+ throw new Error("must have exactly 2 children");
26
+ }
27
+ return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement("div", { className: classNames(classes.child, classes.firstChild) }, childArray[0]), /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }), /* @__PURE__ */ React.createElement("div", { className: classes.child }, childArray[1]));
28
+ }
29
+
30
+ export { DryRunResultsSplitView };
31
+ //# sourceMappingURL=DryRunResultsSplitView.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DryRunResultsSplitView.esm.js","sources":["../../../../src/next/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { makeStyles } from '@material-ui/core/styles';\nimport Divider from '@material-ui/core/Divider';\nimport React, { Children, ReactNode } from 'react';\nimport classNames from 'classnames';\n\nconst useStyles = makeStyles(theme => ({\n root: {\n display: 'grid',\n gridTemplateColumns: '280px auto 3fr',\n gridTemplateRows: '1fr',\n },\n child: {\n overflowY: 'auto',\n height: '100%',\n minHeight: 0,\n },\n firstChild: {\n background: theme.palette.background.paper,\n },\n}));\n\nexport function DryRunResultsSplitView(props: { children: ReactNode }) {\n const classes = useStyles();\n const childArray = Children.toArray(props.children);\n\n if (childArray.length !== 2) {\n throw new Error('must have exactly 2 children');\n }\n\n return (\n <div className={classes.root}>\n <div className={classNames(classes.child, classes.firstChild)}>\n {childArray[0]}\n </div>\n <Divider orientation=\"horizontal\" />\n <div className={classes.child}>{childArray[1]}</div>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;AAqBA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,IAAM,EAAA;AAAA,IACJ,OAAS,EAAA,MAAA;AAAA,IACT,mBAAqB,EAAA,gBAAA;AAAA,IACrB,gBAAkB,EAAA,KAAA;AAAA,GACpB;AAAA,EACA,KAAO,EAAA;AAAA,IACL,SAAW,EAAA,MAAA;AAAA,IACX,MAAQ,EAAA,MAAA;AAAA,IACR,SAAW,EAAA,CAAA;AAAA,GACb;AAAA,EACA,UAAY,EAAA;AAAA,IACV,UAAA,EAAY,KAAM,CAAA,OAAA,CAAQ,UAAW,CAAA,KAAA;AAAA,GACvC;AACF,CAAE,CAAA,CAAA,CAAA;AAEK,SAAS,uBAAuB,KAAgC,EAAA;AACrE,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAA,MAAM,UAAa,GAAA,QAAA,CAAS,OAAQ,CAAA,KAAA,CAAM,QAAQ,CAAA,CAAA;AAElD,EAAI,IAAA,UAAA,CAAW,WAAW,CAAG,EAAA;AAC3B,IAAM,MAAA,IAAI,MAAM,8BAA8B,CAAA,CAAA;AAAA,GAChD;AAEA,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,OAAA,CAAQ,IACtB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,KAAI,EAAA,EAAA,SAAA,EAAW,UAAW,CAAA,OAAA,CAAQ,KAAO,EAAA,OAAA,CAAQ,UAAU,CACzD,EAAA,EAAA,UAAA,CAAW,CAAC,CACf,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,OAAQ,EAAA,EAAA,WAAA,EAAY,cAAa,CAClC,kBAAA,KAAA,CAAA,aAAA,CAAC,KAAI,EAAA,EAAA,SAAA,EAAW,OAAQ,CAAA,KAAA,EAAA,EAAQ,UAAW,CAAA,CAAC,CAAE,CAChD,CAAA,CAAA;AAEJ;;;;"}